在使用事务的时候需要添加@EnableTransactionManagement注解来开启事务,那么就从@EnableTransactionManagement入手查看一下事务的执行原理。
@EnableTransactionManagement
- Spring事务底层是通过AOP来完成的,而Spring AOP基于动态代理实现,可以看到mode方法默认返回了PROXY代理模式,我们只需关注代理模式下的执行流程即可
- 使用@Import导入了TransactionManagementConfigurationSelector
1 | (ElementType.TYPE) |
TransactionManagementConfigurationSelector
在selectImports方法中可以看到对模式进行了判断:
- 如果是基于代理模式,返回AutoProxyRegistrar和ProxyTransactionManagementConfiguration类
- 如果是基于ASPECTJ,调用determineTransactionAspectClass方法
Spring默认使用的是代理模式,所以接下来看下AutoProxyRegistrar和ProxyTransactionManagementConfiguration里面都有什么。
1 | public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { |
AutoProxyRegistrar
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar可以向容器中注册Bean,跟着registerBeanDefinitions方法看下它会向容器中注册什么样的bean:
1 | public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { |
AopConfigUtils
在AopConfigUtils中一共有三种自动代理创建器:
- InfrastructureAdvisorAutoProxyCreator
- AspectJAwareAdvisorAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator
在registerAutoProxyCreatorIfNecessary方法中,可以看到事务使用的是InfrastructureAdvisorAutoProxyCreator类型的创建器:
1 | public abstract class AopConfigUtils { |
总结
AutoProxyRegistrar实现ImportBeanDefinitionRegistrar是为了向容器中注册代理创建器,事务默认使用的是InfrastructureAdvisorAutoProxyCreator类型的。
ProxyTransactionManagementConfiguration
1. AOP概念
Advice通知:定义在切点上需要执行什么样的操作
PointCut切点:定义在哪些方法上使用通知
Advisor:Advice和Pointcut加起来组成了Advisor
2. 事务中的Advisor
我们已经知道事务是基于AOP实现的,在transactionAdvisor方法中可以看到创建了Advisor,然后设置了事务属性TransactionAttributeSource和事务拦截器TransactionInterceptor:
- TransactionAttributeSource,从名字上可以看出是和事务的属性设置相关的
- TransactionInterceptor事务拦截器相当于Advice通知
- BeanFactoryTransactionAttributeSourceAdvisor是Advisor
Advisor由Advice和PointCut组成,现在Advice已经有了,接下来看下Pointcut在哪里。
1 | false) (proxyBeanMethods = |
BeanFactoryTransactionAttributeSourceAdvisor
BeanFactoryTransactionAttributeSourceAdvisor继承关系如下:
除了继承父类的属性和方法,它自己还有两个成员变量:
- transactionAttributeSource,实际传入的是AnnotationTransactionAttributeSource类型的对象
- TransactionAttributeSourcePointcut类型的切点pointcut
- 切点在实例化时实现了getTransactionAttributeSource方法,返回了transactionAttributeSource,后面的方法中需要调用此方法获取transactionAttributeSource
由上面的分析可知,在创建BeanFactoryTransactionAttributeSourceAdvisor的时候,设置了TransactionInterceptor和TransactionAttributeSource,TransactionInterceptor相当于Advice,而这里我们看到了它还有一个TransactionAttributeSourcePointcut切点:
1 | public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor { |
TransactionAttributeSourcePointcut
TransactionAttributeSourcePointcut是一个切点,它的继承关系如下:
Pointcut和MethodMatcher
Pointcut接口中定义了两个方法:
- 获取ClassFilter,ClassFilter是一个接口,里面定义了matches方法,检查切点是否与类匹配
- 获取MethodMatcher,它也是一个接口,并且定义了matches方法,检查切点是否与方法匹配
1 | public interface Pointcut { |
TransactionAttributeSourcePointcut是Pointcut和MethodMatcher的子类:
- 在构造函数中设置了ClassFilter,类型为TransactionAttributeSourceClassFilter,它是TransactionAttributeSourcePointcut的一个内部类,实现了ClassFilter接口中定义的matches方法,检查pointcut与类是否匹配:
- 如果是TransactionalProxy、PlatformTransactionManager或者PersistenceExceptionTranslator的子类,则不匹配
- 获取TransactionAttributeSource,调用它的isCandidateClass方法判断是否匹配
- 实现了MethodMatcher接口中定义的matches方法,检查pointcut是否匹配当前的方法
- 获取TransactionAttributeSource判断是否为空,如果不为空则调用getTransactionAttribute获取事务属性,TransactionAttributeSource为空,或者从TransactionAttributeSource获取到的事务属性不为空都会返回true
所以一个方法执行时开启事务,需要满足两个条件,当前的方法和类都需要与事务的pointcut匹配,对应的方法分别是MethodMatcher的matches和ClassFilter的matches方法。
1 | abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { |
AnnotationTransactionAttributeSource
条件一:检查类是否匹配事务切点
上面分析可知,检查类是否与切点匹配时获取了TransactionAttributeSource,调用它的isCandidateClass方法进行判断,TransactionAttributeSource的具体实现是AnnotationTransactionAttributeSource:
- 在构造方法中,添加了注解解析器:
- Spring事务注解解析器的实现类为SpringTransactionAnnotationParser,也是默认的注解解析器。
- 如果开启了JTA或者EJB,将会分别添加对应的解析器。
- 实现了isCandidateClass方法,实际又是调用注解解析器的isCandidateClass判断是否是候选类的。
1 | "serial") ( |
SpringTransactionAnnotationParser
SpringTransactionAnnotationParser实现了isCandidateClass方法,它又调用了AnnotationUtils的isCandidateClass判断目标类是否是Transactional注解的候选类,AnnotationUtils中isCandidateClass的具体判断逻辑如下:
如果注解类路径以java.开头,返回true,这里Transactional注解不是java.开头,它是Spring的注解类,所以这个条件不会成立
如果目标类的类路径以java.开头,或者是Ordered类型,isCandidateClass返回false,说明目标类不是某个注解的候选类
除去以上两种情况之外,isCandidateClass都返回true
总结: 如果目标类的类路径不以java.开头(也就是说它不是java的相关类),也不是Ordered类型,说明目标类是Transactional注解的候选类。
SpringTransactionAnnotationParser实现了parseTransactionAnnotation方法,里面包含对事物属性的解析。
1 | public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { |
条件二:检查方法是否匹配事务切点
AbstractFallbackTransactionAttributeSource
如果从TransactionAttributeSource获取到的事务属性不为空将会满足切点的匹配条件,获取事务属性的方法实现在AbstractFallbackTransactionAttributeSource类中:
- 如果当前方法是Object中的方法,返回空
- 根据当前的方法和类的信息构建缓存key,从缓存中获取
- 如果获取不为空,判断是否为空的事务属性NULL_TRANSACTION_ATTRIBUTE,如果是则返回null,否则返回从缓存中获取到的事务属性
- 如果获取为空,调用解析事务属性的方法进行解析,然后放入缓存中并返回
1 | public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource { |
总结
事务是基于AOP实现的,事务的Advisor是BeanFactoryTransactionAttributeSourceAdvisor,Advisor判断方法是否匹配时,是通过Pointcut的matches方法判断的,事务的Pointcut是TransactionAttributeSourcePointcut,里面实现了方法是否与事务切点匹配的判断:
对类的匹配是通过判断目标类是否是Transactional注解的候选类实现的,我们创建的类一般不会以java.开头,所以说可以与Transactional注解匹配成功。
对方法的匹配是通过解析方法上面配置的事务属性判断的,如果解析到了事务属性,则满足匹配条件。
TransactionInterceptor
TransactionInterceptor是事务Advisor的Advice,执行目标方法时,方法会被拦截,进入到TransactionInterceptor中,在TransactionInterceptor的invoke方法中实际是调用invokeWithinTransaction执行的:
1 | public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { |
TransactionAspectSupport
TransactionAspectSupport中实现了invokeWithinTransaction方法:
- 获取事务属性TransactionAttribute和TransactionManager事务管理器
- 对响应式事务、声明式事务和编程式事务分别进行判断,以声明式事务为例步骤如下:
- 创建事务
- 执行方法
- 捕捉异常,如果抛出异常进行回滚
- 清除事务信息
- 提交事务
1 | public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { |
总结
参考
Spring版本:5.2.5.RELEASE