阅读此文章需要掌握一定的AOP源码基础知识,可以更好的去理解事务,我在另外一篇文章有提过。
spring事务其实就是根据事务注解生成代理类,然后在前置增强方法里获取connection,设置
connection
到threadlocal
,开启事务。再执行原始方法,最后在后置增强方法中判断有无异常来进行事务回滚或提交,再释放连接。
对Spring中的事务功能的代码进行分析,我们先从配置文件开始入手:在配置文件中我们是通过tx:annotation-driven
的方式开启的事务配置,所以我们先从这里开始进行分析,根据以往的经验我们在自定义标签中的解析过程中一定是做了一些操作,于是我们先从自定义标签入手进行分析。使用IDEA搜索全局代码,关键字annotation-driven
,最终锁定在类TxNamespaceHandler
主要查看TxNameSpaceHandler
的init
方法。
- 看源码(
TxNamespaceHandler.java
)
1 | java复制代码@Override |
- 源码分析
在上述源码中我们看到了annotation-driven
这个标识,该行代码也就是说:在遇到诸如tx:annotation-driven
为开头的配置后,Spring都会使用AnnotationDrivenBeanDefinitionParser
类的parse
方法进行解析处理。我们接下来跟踪AnnotationDrivenBeanDefinitionParser
类的parse
方法
- 看源码(
AnnotationDrivenBeanDefinitionParser.java
)
1 | java复制代码@Override |
- 源码分析
在上面的源码中我们可以看到有对mode属性进行一个解析判断,根据代码,如果我们需要使用Aspect的方式进行事务的切入(Spring中的事务是以AOP为基础的),根据源码的判断条件我们可以看出我们在开始事务配置的时候也可以像如下这种方式进行开启:
1 | xml复制代码<!--开启tx注解--> |
事务代理类的创建
1 | go复制代码根据上面我们也可以知道,Spring事务会根据配置的`mode`不同,会有不同的实现。我们分开探索: |
aspectj模式
- 看方法registerJtaTransactionAspect
1 | java复制代码private void registerTransactionAspect(Element element, ParserContext parserContext) { |
看一下AnnotationTransactionAspect
类图
我们发现AnnotationTransactionAspect和父类AbstractTransactionAspect都不是正常class,是aspect;之前设置的transactionManagerBeanName属性在TransactionAspectSupport
中
接下来继续追踪AnnotationTransactionAspect
类
其实这里这种用法和@Aspect切面类里定义@Pointcut,@Around的用法一个效果
- 看源码(
AnnotationTransactionAspect.java
)
1 | java复制代码public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { |
- 看源码(
AbstractTransactionAspect.java
)
1 | java复制代码public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean { |
总结
aspectj模式其实就是定义了一个Aspect,里面定义了切点,针对所有@Transaction注解的方法,并对切点进行@Around增强,会调用父类
TransactionAspectSupport
的invokeWithinTransaction
方法。
proxy模式
我们从默认的配置方式进行分析(也就是不加mode="aspect"
的方式)。对AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
进行分析。首先我们先看一下AopAutoProxyConfigurer
类的configureAutoProxyCreator
方法:
AopAutoProxyConfigurer类属于AnnotationDrivenBeanDefinitionParser的内部类
- 源码分析(
AnnotationDrivenBeanDefinitionParser.java
)
1 | java复制代码private static class AopAutoProxyConfigurer { |
- 源码分析
看上述源码简单总结一下:这里主要是注册了三个Bean,分别是AnnotationTransactionAttributeSource
、TransactionInterceptor
、BeanFactoryTransactionAttributeSourceAdvisor
,还注册了一个InfrastructureAdvisorAutoProxyCreator
的bean;其中前三个Bean支撑了整个事务的功能。在这里我们简单回顾一下AOP原理:
AOP原理:
AOP中有一个Advisor
存放在代理类中,而Advisor中有advise
与pointcut
信息,每次执行被代理类的方法时都会执行代理类的invoke
(如果是JDK代理)方法,而invoke方法会根据advisor中的pointcut动态匹配这个方法需要执行的advise链,遍历执行advise链,从而达到AOP切面编程的目的。
需要注意的地方BeanFactoryTransactionAttributeSourceAdvisor.class
,首先看到这个类的继承结构,可以看的出来它其实是一个Advisor
,这个类中有几个关键的地方需要注意一下,在前面提到的一共注册了三个Bean,将AnnotationTransactionAttributeSource
、TransactionInterceptor
这两个属性注入到了这个bean中:代码如下:
1 | csharp复制代码// 将上面 AnnotationTransactionAttributeSource 类型Bean注入进上面的Advisor |
问题来了:它们被注入成了什么呢?进入到BeanFactoryTransactionAttributeSourceAdvisor
一看便知
1 | java复制代码@Nullable |
在BeanFactoryTransactionAttributeSourceAdvisor
父类中AbstractBeanFactoryPointcutAdvisor
有这样的一个属性
1 | java复制代码@Nullable |
从上面可以大概知道,先将TransactionInterceptor
的BeanName传入到注入到Advisor
,然后将AnnotationTransactionAttributeSource
这个Bean注入到Advisor
。那么这个Source Bean有什么用呢?我们继续追踪BeanFactoryTransactionAttributeSourceAdvisor.java
的源码:
- 看源码(
BeanFactoryTransactionAttributeSourceAdvisor.java
)
1 | java复制代码private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() { |
- 源码解析
看到这里应该明白了,这里的Source提供了Pointcut信息,作为存放事务属性的一个类注入到Advisor中。进行到这里我们也知道了这三个BeanAnnotationTransactionAttributeSource
、TransactionInterceptor
、BeanFactoryTransactionAttributeSourceAdvisor
,的作用了。简单来说就是先注册pointcut、advice、advisor,然后将pointcut和advice注入到advisor中,在之后动态代理的时候会使用这个Advisor去寻找每个Bean是否需要动态代理(取决与是否有开启事务),因为Advisor中有pointcut信息。
InfrastructureAdvisorAutoProxyCreator
:在方法的开头,首先调用了AopConfigUtils
去注册了这个Bean,那么这个Bean是做什么的?首先还是看一下这个类的结构。
这个类继承了AbstractAutoProxyCreator
,看到这个名字,结合之前说过的AOP的应该知道它是做什么的了。其次这个类还实现了BeanPostProcessor
接口,凡是实现了这个BeanPostProcessor接口的类,我们首先关注的就是它的postProcessAfterInitialization
方法,这个在其父类也就是刚刚提到的AbstractAutoProxyCreator
去实现的(这里需要知道Spring容器初始化Bean的过程,关于BeanPostProcessor的使用后续讲解,如果不知道只需要了解如果一个Bean实现了BeanPostProcessor接口,当所有Bean实例化且依赖注入之后初始化方法之后会执行这个实现Bean的postProcessAfterInitialization方法)。
接下来进入这个函数:
- 看源码(
AopNamespaceUtils.java
)
1 | java复制代码public static void registerAutoProxyCreatorIfNecessary( |
继续追踪上面的源码中的registerComponentIfNecessary
方法
- 看源码(
AopNamespaceUtils.java
)
1 | java复制代码private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) { |
- 源码分析
对于上面解析来的代码流程已经在AOP中有所分析,可以自行翻看AOP的文章,上面的两个函数主要的目的是注册了InfrastructureAdvisorAutoProxyCreator
类型的Bean,那么注册这个类的目的是什么呢?再次查回顾这个类的层次结构:
分析这个类的层次结构:InfrastructureAdvisorAutoProxyCreator
这个类间接实现了SmartInstantiationAwareBeanPostProcessor
接口,而SmartInstantiationAwareBeanPostProcessor这个接口有继承了InstantiationAwareBeanPostProcessor
接口。也就是说在Spring中,所有Bean实例化时Spring都会保证调用其postProcessAfterInstantiation
方法。其实现是在其父类AbstractAutoProxyCreator
中。
接下来一之前AccountByXMLServiceImpl
为例,当实例化AccountByXmlServiceImpl
的Bean时便会调用下面这个方法,方法如下:
1 | java复制代码@Override |
这里实现的主要目的就是对指定的Bean进行封装,当然首先要确定是否需要封装,检测与封装的工作都委托给了wrapIfNecessary
函数进行:
1 | java复制代码protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { |
wrapIfNecessary函数看上去比较复杂,但是逻辑相对还是比较简单的,在wrapIfNecessary函数中主要的工作如下:
- 找出指定bean对应的增强器
- 根据找出的增强器创建代理
听起来挺简单的逻辑,但是Spring又做了哪些复杂的工作呢?对于创建代理的工作,通过之前AOP的文章分析相信大家已经有所熟悉了。但是对于增强器的获取,Spring又是如何操作的呢?
获取对应class/method的增强器
寻找候选的增强
获取指定Bean对应的增强器,其中包含了两个关键字:增强器与对应。也就是说在getAdvicesAndAdvisorsForBean
函数(上面wrapIfNecessary函数里面的方法)中,不仅要找出增强器,而且要判断增强器是否满足要求。接下来看一下源码:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
1 | java复制代码@Override |
继续查看一下上面方法中的findEligibleAdvisors函数:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
1 | java复制代码protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { |
在findEligibleAdvisors
函数中我们发现了findCandidateAdvisors
和findAdvisorsThatCanApply
这两个函数;其中findCandidateAdvisors
这个函数是寻找候选的增强,我们简单扫一下这个源码:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
1 | java复制代码protected List<Advisor> findCandidateAdvisors() { |
接着追踪里面的findAdvisorBeans
函数
- 看源码(
BeanFactoryAdvisorRetrievalHelper.java
)
1 | java复制代码public List<Advisor> findAdvisorBeans() { |
- 源码分析
首先这个findAdvisorBeans
函数先通过BeanFactoryUtils类提供的工具方法获取对应的Advisor.class,获取的办法就是使用ListableBeanFactory
中提供的方法:
1 | java复制代码String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); |
在我们讲解自定义标签时曾经注册了一个类型为BeanFactoryTransactionAttributeSourceAdvisor
的Bean,而在此bean中我们又注入了另外两个Bean,那么此时这个Bean就会开始被使用,因为BeanFactoryTransactionAttributeSourceAdvisor同样也实现了Advisor接口。那么在获取所有增强器时自然也会将此Bean提取出来,并随着其他增强器一起在后续的步骤中被植入代理。
候选增强器中寻找匹配项
当找出对应的增强器后,接下来的任务就是看这些增强器是否与对应的class匹配了,当然不只是class,class内部的方法如果匹配也可以通过验证。
接下来看在findEligibleAdvisors
函数中我的findAdvisorsThatCanApply
这个函数:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
1 | java复制代码protected List<Advisor> findAdvisorsThatCanApply( |
继续追踪里面的findAdvisorsThatCanApply
方法
- 看源码(
AopUtils
)
1 | java复制代码public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { |
继续追踪该方法中的canApply方法:
1 | java复制代码public static Boolean canApply(Advisor advisor, Class<?> targetClass, Boolean hasIntroductions) { |
- 源码分析
回想之前,因为BeanFactoryTransactionAttributeSourceAdvisor
间接实现了PointcutAdvisor
。
所以在canApply函数中的第二个if判断是就会通过判断。会将BeanFactoryTransactionAttributeSourceAdvisor中的getPointcut() 方法返回值作为参数继续调用canApply方法,而getPoint() 方法返回的是TransactionAttributeSourcePointcut实例,对于transactionAttributeSource这个属性相信大家还有印象,就是在解析自定义标签时注入进去的,方法如下
1 | java复制代码private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() { |
那么,使用TransactionAttributeSourcePointcut
类型的实例作为函数继续追踪canApply
- 源码(
AopUtils.java
)
1 | java复制代码public static Boolean canApply(Pointcut pc, Class<?> targetClass, Boolean hasIntroductions) { |
通过上面的函数大致可以理清大致的脉络:
首先获取对应类的所有接口并连通本类一起遍历,遍历过程中又对类中的方法再次遍历,一旦匹配成功便认为这个类适用于当前增强器。
到这里我们就会有疑问?**对于事务的配置不仅仅局限在函数配置上,我们都知道,在类或接口的配置上可以延续到类中的每个函数上。那么,如果针对每个函数进行检测,在本类身上配置的事务属性岂不是检测不到了吗?**接下来我们带着这个疑问继续探索canApply函数中的
matcher
的方法。
做匹配的时候
methodMatcher.matches(method, targetClass)
会使用TransactionAttributeSourcePointcut
类的matches
方法。
- 看源码(
TransactionAttributeSourcePointcut.java
)
1 | java复制代码@Override |
- 源码分析
此时上述源码中的tas标识AnnotationTransactionAttributeSource
类型,这里会判断tas.getTransactionAttribute(method, targetClass) ,而AnnotationTransactionAttributeSource 类型的getTransactionAttribute
方法如下:
- 看源码(
AbstractFallbackTransactionAttributeSource.java
)
1 | java复制代码@Override |
- 源码分析
尝试从缓存加载,如果对应信息没有缓存的话,工作有委托给了computeTransactionAttribute
函数,在computeTransactionAttribute函数中我们终于看到了事务标签的提取过程。
- 看源码(
AbstractFallbackTransactionAttributeSource.java
)
1 | java复制代码@Nullable |
- 源码分析
对于事务属性的获取规则相信大家都已经熟悉很清楚,如果方法中存在事务属性,则使用方法上的属性,否则使用方法所在类上的属性,如果方法所在类的属性还是没有搜寻到对应的事务属性,那么搜寻接口中的方法,再没有的话,最后尝试搜寻接口类上面的声明。对于函数computeTransactionAttribute
中的逻辑与我们所认识的规则并无差别,但是上面函数中并没有真正的去做搜寻事务属性的逻辑,而是搭建了个执行框架,将搜寻事务属性的任务委托给了findTransactionAttribute
方法去执行。继续进行分析:
- 看源码(
AnnotationTransactionAttributeSource.java
)
1 | java复制代码@Override |
继续查看上面的determineTransactionAttribute
函数:
- 看源码(
AnnotationTransactionAttributeSource.java
)
1 | java复制代码@Nullable |
- 源码分析
this.annotationParsers是在当前类AnnotationTransactionAttributeSource 初始化的时候初始化的,其中的值被加入了SpringTransactionAnnotationParser
,也就是当进行属性获取的时候虎其实是使用可SpringTransactionAnnotationParser
类的parseTransactionAnnotation
方法进行解析的。继续分析源码
- 看源码(
SpringTransactionAnnotationParser.java
)
1 | java复制代码@Override |
- 源码分析
至此,我们终于看到了想看到的获取注解标记的代码,首先会判断当前的类是否包含又Transactional
注解,这是事务属性的基础,当然如果有的话会继续调用parseTransactionAnnotation
方法解析详细的属性。接着看源码:
- 看源码()
1 | java复制代码protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { |
- 源码解析
至此,我们终于完成了事务标签的解析,回顾一下,我们现在的任务是找出某个增强其是否适合于对应的类,而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性。现在我们之前的AccountByXMLServiceImpl
为例,已经在它的接口AccountByXMLServiceImpl
中找到了事务属性,所以,它是与事务增强器匹配的,也就是它会被事务功能修饰。
1 | markdown复制代码至此,事务功能的初始化工作便结束了,当判断某个bean适用于事务增强时,也就i是适用于增强器`BeanFactoryTransactionAttributeSourceAdvisor`,**BeanFactoryTransactionAttributeSourceAdvisor**作为Advisor的实现类,自然要遵从Advisor的处理方式,当代理被调用时会调用这个类的增强方法,也就是bean的Advice,又因为在解析事务定义标签时我们把`Transactionlnterceptor`类的Bean注入到了`BeanFactoryTransactionAttributeSourceAdvisor`中,所以,在调用事务增强器增强的代理类时会首先执行**Transactionlnterceptor**进行增强,同时,也就是在Transactionlnterceptor类中的invoke方法中完成了整个事务的逻辑。 |
总结:
这一篇文章主要将了事务的Advisor是如何注册进Spring容器的,也讲解了Spring是如何将有配置事务的类配置上事务的,实际上就是使用了AOP那一套,也讲解了Advisor和Pointcut验证流程。至此事务的初始化工作已经完成,在之后的调用过程,如果代理类的方法被调用,都会调用
BeanFactoryTransactionAttributeSourceAdvisor
这个Advisor的增强方法。目前就是我们还没有提到的那个Advisor里面的Advice;还记得吗我们在自定义标签的时候我们将TransactionInterceptor
这个Advice作为Bean注入到IOC容器中,并且将其注入到Advisor中,这个Advice在代理类的invoke方法中会被封装到拦截器链中,最终事务的功能都能在Advice中体现。
本文转载自: 掘金