欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 第三十一章 Spring之假如让你来写事务——融入IOC容器篇

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

2024/12/21 22:12:58 来源:https://blog.csdn.net/weixin_42789334/article/details/144401386  浏览:    关键词:第三十一章 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:advicetx:attributestx: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 对应着方法拦截器,而拦截器有依赖于 transactionAttributeSourcetransactionManager 这两个属性,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;

整体代码没什么难度,解析对应的标签,并且添加到对应的对象中即可。解析器定义完成之后,还需要注册解析器,需要继承 NamespaceHandlerSupportA君 定义 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君 怀着快乐的心情,开始收拾包裹回家去咯


总结

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com