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&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君 怀着快乐的心情,开始收拾包裹回家去咯
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)