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容器
-
-
- [第二十九版 融入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君 先看下标签,如下:
xml
<!-- 配置事务的建议 -->
<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
方法,代码如下:
java
@Override
public 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
方法。代码如下:
java
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
类,代码如下:
java
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);
}
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
}
}
注册器有了,还要让 容器 只要有这么个玩意,A君 修改 spring.handlers
文件,把对应的注册器注册进去
txt
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
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://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>
相较于之前,这个配置明显简单了许多。有了配置文件,代码有简单了。测试代码如下:
xml
@Test
public 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君 怀着快乐的心情,开始收拾包裹回家去咯
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)