第三十一章 Spring之假如让你来写事务——融入IOC容器篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
    • 第三部分——事务篇
  • 前言
  • 尝试动手写IOC容器
      • 第二十九版 融入IOC容器
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第三十章 Spring之假如让你来写事务——管理篇 中,A君 实现了 事务 全部功能了。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要:A君 实现了 事务 全部功能了 。。。

第二十九版 融入IOC容器

    “可以啊,A君,干的不错,感觉是不是就是顺手的事?” 老大 看着代码,笑着说道

    “还行吧,整体逻辑是还好,只是要去了解 事务 的一些知识,以及考虑对其他框架的内容比较麻烦。” A君 忍不住抱怨到

    “一个成功的框架必然要对主流框架进行兼容,不然就流行不起来了。” 老大 语重心长的说到 “接下来,你还要考虑一件事!”

    “啥,这还不够吗?还有考虑哪里” A君 愕然

    “事务 方面是足够了,但是呢,配置太过繁琐了,简单一点。” 老大 道

    “好趴。” 反正都到这一步了,也不差这一步了。A君 自我安慰道

    怎么和 IOC 融合,在前面做 AOP 的时候,A君 已经有经验了。只需要把 XML 解析成对应的 BeanDefinition 即可。老样子,A君 首先定义 xsd 文件,这个都是一样的,直接从 Spring 中拷贝就行了。接下来,就是解析器了,由于这是拓展标签,所以没有相应的解析器,需要自己实现。A君 先看下标签,如下:

   <!-- 配置事务的建议 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/><tx:method name="update*" propagation="NESTED" rollback-for="java.lang.Exception"/><tx:method name="delete*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/></tx:attributes></tx:advice>

总共算下来就三个标签,tx:advice、tx:attributes、tx:method。逐个解析并注册就行了。A君 先定义 TxAdviceBeanDefinitionParser 类,重写 parse 方法,代码如下:

	@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {String defaultTMName = "transactionManager";/*** 定义TransactionInterceptor*/BeanDefinition ti = new BeanDefinition(TransactionInterceptor.class);String adviceId = element.getAttribute(ADVICE_ID);ti.setId(adviceId);parserContext.registerBeanDefinition(adviceId, ti);/*** 事务管理器*/String transactionManagerName = TxNamespaceHandler.getTransactionManagerName(element);ti.getProperties().add(defaultTMName, new RuntimeBeanReference(transactionManagerName));List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);if (txAttributes.size() == 1) {Element attributeSourceElement = txAttributes.get(0);BeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);ti.getProperties().add("transactionAttributeSource", new RuntimeBeanReference(attributeSourceDefinition.getId()));ti.getProperties().add("transactionManagerBeanName", new RuntimeBeanNameReference(defaultTMName));} else {//TODO}return ti;}

tx:advice 对应着方法拦截器,而拦截器有依赖于 transactionAttributeSource、transactionManager 这两个属性,transactionManager 可以直接从当前标签里边取,而 transactionAttributeSource 则需要解析 tx:attributes 标签。tx:attributes 则是对应的回滚规则,于是乎,A君 趁热打铁,继续实现解析 tx:attributes。新增 parseAttributeSource 方法。代码如下:

private BeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {/*** 获取method标签*/List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =new ManagedMap<>(methods.size());for (Element methodEle : methods) {//解析方法名String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);TypedStringValue nameHolder = new TypedStringValue(name);/*** 解析事务传播行为* 解析事务超时时间* 解析事务隔离级别* 解析是否只读*/RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);if (StringUtils.isNotBlank(propagation)) {attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);}if (StringUtils.isNotBlank(isolation)) {attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);}if (StringUtils.isNotBlank(timeout)) {attribute.setTimeoutString(timeout);}if (StringUtils.isNotBlank(readOnly)) {attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));}List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(1);if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);addRollbackRuleAttributesTo(rollbackRules, rollbackForValue);}if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);addNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue);}attribute.setRollbackRules(rollbackRules);transactionAttributeMap.put(nameHolder, attribute);}BeanDefinition attributeSourceDefinition = new BeanDefinition(NameMatchTransactionAttributeSource.class);attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);parserContext.registerWithGeneratedName(attributeSourceDefinition);return attributeSourceDefinition;

整体代码没什么难度,解析对应的标签,并且添加到对应的对象中即可。解析器定义完成之后,还需要注册解析器,需要继承 NamespaceHandlerSupport,A君 定义 TxNamespaceHandler 类,代码如下:

public class TxNamespaceHandler extends NamespaceHandlerSupport {static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";static String getTransactionManagerName(Element element) {return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);}@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());}}

注册器有了,还要让 容器 只要有这么个玩意,A君 修改 spring.handlers 文件,把对应的注册器注册进去

http\://www.springframework.org/schema/aop=com.hqd.ch03.v29.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/tx=com.hqd.ch03.v29.tx.transaction.config.TxNamespaceHandler

好了,就这些东西了,接下来又到了快乐的测试环节了,整体代码没什么变化,主要是配置文件变了,A君 定义 bean,xml 文件,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="root"/><property name="initialSize" value="5"/><property name="minIdle" value="5"/><property name="maxActive" value="20"/><property name="validationQuery" value="SELECT 1"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="true"/><property name="testOnReturn" value="false"/></bean><bean id="jdbcTemplate" class="com.hqd.ch03.v29.jdbc.core.SimpleJdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="userService" class="com.hqd.ch03.bean.jdbc.v29.UserServiceImplV29"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="transactionManager" class="com.hqd.ch03.v29.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><aop:config expose-proxy="true"><aop:pointcut id="txPoint" expression="execution(* com.hqd.ch03.bean.jdbc.v29.UserServiceImplV29.*Data(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/><tx:method name="update*" propagation="NESTED" rollback-for="java.lang.Exception"/><tx:method name="delete*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/></tx:attributes></tx:advice>
</beans>

相较于之前,这个配置明显简单了许多。有了配置文件,代码有简单了。测试代码如下:

	@Testpublic void v29() throws Throwable {System.out.println("############# 第二十九版: 融入IOC篇 #############");com.hqd.ch03.v29.AbstractSpringImitation beanFactory =new com.hqd.ch03.v29.SpringImitationXml("classpath:v29/bean.xml");UserServiceImplV29 userService = (UserServiceImplV29) beanFactory.getBean("userService");System.out.println(userService);com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();user.setId(1);user.setSex("nv");user.setName("ls");int i = new Random().nextInt(100);System.out.println(i);user.setAge(i);userService.updateData(user);}

测试结果如下:

修改前:

在这里插入图片描述
修改后:

在这里插入图片描述

    好了,这下彻底完事了,终于可以下班了。A君 怀着快乐的心情,开始收拾包裹回家去咯


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/pingmian/64040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】记录一个python初见的语法(一)

在看书里的代码时了解了一个python的语法知识&#xff0c;是有关class类的。 代码如下&#xff1a; class Vocab: #save"""文本词表"""def __init__(self, tokensNone, min_freq0, reserved_tokensNone):if tokens is None:tokens []if rese…

LabVIEW面向对象编程有什么特点?

LabVIEW面向对象编程&#xff08;OOP&#xff09;的特点主要体现在它如何结合传统面向对象编程&#xff08;OOP&#xff09;的理念与LabVIEW的图形化编程模式&#xff0c;提供灵活的抽象和模块化的功能。以下是LabVIEW面向对象编程的几个主要特点&#xff1a; ​ 1. 类&#x…

【洛谷】P11169 「CMOI R1」Bismuth / Linear Sieve 的题解

【洛谷】P11169 「CMOI R1」Bismuth / Linear Sieve 的题解 洛谷传送门 题解 赛时没调出来&#xff0c;血亏。 先把题目里面的伪代码变成 C 代码 #include <bits/stdc.h> using namespace std;unsigned long long n; bool isNotPrime[10000005]; int primes[1000000…

【LC】240. 搜索二维矩阵 II

题目描述&#xff1a; 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,…

一级考试真题(2019)

一级考试真题&#xff08;2019&#xff09;

AudioSegment 将音频分割为指定长度时间片段 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

【kubernetes】k8s集群的简述与搭建

简述 Kubernetes&#xff08;简称 K8s&#xff09;是一个开源的容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序 关键特性 自动化部署和回滚&#xff1a;Kubernetes 可以自动化地部署和回滚应用程序&#xff0c;确保应用程序始终处于预期的状态。服务发现…

C++ 内存管理和模板与STL

此篇目是之后各种C库的基础 目录 内存管理 内存分布 内存管理方式 new和delete operator new 与 operator delete函数 实现原理 定位new表达式(placement-new) 模板基础 泛型编程 模板 函数模板 类模板 STL 组成部分 内存管理 内存分布 int globalVar 1; //全局变量 静…

在 Ubuntu 24.04.1 LTS (WSL) 中使用 openssl 生成 keybox.xml

看到“生成 keybox.xml”&#xff0c;大概率都会联想到 PIF 和 Tricky Store。这里就不多解释它们的用途了。最近在网上看到生成非 AOSP keybox 的教程&#xff0c;在这里做一些补充&#xff0c;并将代码打包成一个 Python 脚本。 参考自&#xff1a; Idea 提供者&#xff1a…

全局注册快捷键方案

在低代码系统中&#xff0c;快捷键是很常用的操作&#xff0c;我们如果只对绘图区域注册快捷键&#xff0c;那么焦点不在绘图区域时&#xff0c;就会失去快捷键响应&#xff0c;如果对全局拦截键盘事件&#xff0c;注册快捷键&#xff0c;那么会失去一些本应该交给系统默认快捷…

Redis篇-1--入门介绍

1、Redis概述 ‌Redis&#xff08;Remote Dictionary Server&#xff09;&#xff0c;全称为远程字典服务。‌是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。 Redis提供了多种数据类型的存储&#xff0c;来适应不同场景下的存储需…

antdv-<a-button>中属性的使用

UI组件库&#xff08;User Interface Component Library&#xff09;是一种预先构建好的、可重用的用户界面元素集合&#xff0c;旨在帮助开发者更快速、更简便地构建用户界面。这些组件通常包括按钮、表单、导航栏、模态框等&#xff0c;能够提供一致的外观和交互风格&#xf…

简单的多网卡选择指定网卡ip注册

简单的多网卡选择指定网卡ip注册 我们公司服务器上面有多个网卡&#xff0c;多网卡则本地ip有多个ip,我们启动服务的时候需要选定他特定的ip&#xff0c;我们服务需要特定的ip进行注册&#xff0c;才能进行正常的通讯功能&#xff0c;我们需要使用如下配置进行特定ip选择&…

鸿蒙NEXT开发案例:颜文字搜索器

【引言】 本文将介绍一个名为“颜文字搜索器”的开发案例&#xff0c;该应用是基于鸿蒙NEXT平台构建的&#xff0c;旨在帮助用户快速查找和使用各种风格的表情符号。通过本案例的学习&#xff0c;读者可以了解如何在鸿蒙平台上进行数据处理、UI设计以及交互逻辑的实现。 【环…

快速部署一套K8s集群-v1.28

快速部署一套K8s集群-v1.28 1.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadmKubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。 二进制包从github下载发行版的二进…

【硬件测试】基于FPGA的4ASK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章: 《基于FPGA的4ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR》 的…

用友U8+ API接口使用教程

前言 U8和其他的公开的开放API接口有一些差异&#xff0c;他是需要先对接的到代理服务器&#xff0c;通过代理服务器进行对接&#xff0c;所以只要保证U8能上网就能对接&#xff0c;和畅捷通T的模式有点类似 流程&#xff1a; 注册成为开发者&#xff08;用于创建用友U8 API应…

c# TaskScheduler

这里记录下 TaskScheduler 的简单用法。 使用场景&#xff1a; 使用 Task 的时候&#xff0c;大家知道用 TaskFactory.StartNew 可以用来创建一个 Task 。这里如果创建了 3 个&#xff0c;那么这3个 Task 就各自放飞直接运行了。 class Program {private static TaskFactory…

Go语言错误分类

错误的分类 在 Go 语言中&#xff0c;错误是通过实现 error 接口的类型表示的&#xff0c;但不同场景下的错误可以按性质和用途进行分类。以下是 Go 语言错误的常见分类&#xff0c;以及每类错误的解释和示例&#xff1a; 标准错误类型 标准库中定义了许多常见的错误类型&…

Couchbase的OLAP支持情况

Couchbase 是一个高性能的 NoSQL 数据库&#xff0c;主要用于在线事务处理&#xff08;OLTP&#xff09;场景&#xff0c;但它也提供了一些功能来支持在线分析处理&#xff08;OLAP&#xff09;需求。以下是 Couchbase 对 OLAP 支持的几个方面&#xff1a; 1. N1QL 查询语言 …