spring-transaction源码分析(2)EnableTransactionManagement注解

打印 上一主题 下一主题

主题 785|帖子 785|积分 2355

概述(Java doc)

该注解开启spring的注解驱动事务管理功能,通常标注在@Configuration类上面用于开启命令式事务管理或响应式事务管理。
  1. @Configuration
  2. @EnableTransactionManagement
  3. public class AppConfig {
  4.     @Bean
  5.     public FooRepository fooRepository() {
  6.         // configure and return a class having @Transactional methods
  7.         return new JdbcFooRepository(dataSource());
  8.     }
  9.     @Bean
  10.     public DataSource dataSource() {
  11.         // configure and return the necessary JDBC DataSource
  12.     }
  13.     @Bean
  14.     public PlatformTransactionManager txManager() {
  15.         return new DataSourceTransactionManager(dataSource());
  16.     }
  17. }
复制代码
The example above can be compared to the following Spring XML configuration:
  1. <beans>
  2.     <tx:annotation-driven/>
  3.     <bean id="fooRepository" >
  4.         <constructor-arg ref="dataSource"/>
  5.     </bean>
  6.     <bean id="dataSource" />
  7.     <bean id="transactionManager" >
  8.         <constructor-arg ref="dataSource"/>
  9.     </bean>
  10. </beans>
复制代码
添加该注解之后,spring-tx会注册必要的spring组件,这些组件支持注解驱动的事务管理,例如TransactionInterceptor和基于proxy(jdk/cglib)或aspectJ的advice,当调用@Transactional标注的方法时,这些advice将通过拦截器被调用。
注解属性

proxyTargetClass
  1. boolean proxyTargetClass() default false;
复制代码
设置为true时,创建基于子类的(CGLIB)代理。设置为false时,使用JDK Proxy创建代理。默认值为false。
该属性仅在mode属性设置为AdviceMode.PROXY时生效。
将此属性设置为true将影响所有需要代理的spring bean,而不仅仅是那些用@Transactional标记的bean。
例如,标记有@Async注解bean将同时升级为子类(CGLIB)代理,这个特性实际上没有负面影响,除非明确期望使用某种类型的代理。
mode
  1. /**
  2. * PROXY: JDK/CGLIB proxy-based advice
  3. * ASPECTJ: AspectJ weaving-based advice
  4. */
  5. AdviceMode mode() default AdviceMode.PROXY;
复制代码
指明使用哪种代理方式嵌入事务通知。
默认AdviceMode.PROXY模式。
PROXY模式只允许通过代理对象调用。同一个类中的本地调用(即this.方法名方式)不能被拦截,本地调用时Transactional注解不会生效,因为spring aop在拦截逻辑执行之后使用原始bean对象调用目标方法,所以this.方法名方式调用会使Transactional注解失效。如果要解决这个问题,可以考虑将其切换到AdviceMode.ASPECTJ。
如果设置为AdviceMode.ASPECTJ模式,proxyTargetClass属性的值将被忽略。在这种情况下,需要依赖spring-aspects模块,该模块又依赖了AspectJ,AspectJ会在编译或加载时将事务拦截逻辑应用到@Transactional标记的类。在这种情况下没有代理,本地调用也将被拦截。
order
  1. // Integer.MAX_VALUE
  2. int order() default Ordered.LOWEST_PRECEDENCE;
复制代码
指示在特定joinpoint应用多个通知时执行事务通知的顺序。
EnableTransactionManagement源码
  1. @Import(TransactionManagementConfigurationSelector.class)
  2. public @interface EnableTransactionManagement {
  3.         boolean proxyTargetClass() default false;
  4.         AdviceMode mode() default AdviceMode.PROXY;
  5.         int order() default Ordered.LOWEST_PRECEDENCE;
  6. }
复制代码
TransactionManagementConfigurationSelector
  1. public class TransactionManagementConfigurationSelector
  2.     extends AdviceModeImportSelector<EnableTransactionManagement> {
  3.         @Override
  4.         protected String[] selectImports(AdviceMode adviceMode) {
  5.                 switch (adviceMode) {
  6.                         case PROXY:
  7.                                 return new String[] {AutoProxyRegistrar.class.getName(),
  8.                                                 ProxyTransactionManagementConfiguration.class.getName()};
  9.                         case ASPECTJ:
  10.                                 return new String[] {determineTransactionAspectClass()};
  11.                         default:
  12.                                 return null;
  13.                 }
  14.         }
  15.         private String determineTransactionAspectClass() {
  16.                 return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
  17.                                 TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
  18.                                 TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
  19.         }
  20. }
复制代码
PROXY模式

EnableTransactionManagement注解的mode属性设置为PROXY模式(默认)时,会Import两个组件:

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration
AutoProxyRegistrar
  1. Object mode = candidate.get("mode");
  2. Object proxyTargetClass = candidate.get("proxyTargetClass");
  3. if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
  4.                 Boolean.class == proxyTargetClass.getClass()) {
  5.         candidateFound = true;
  6.         if (mode == AdviceMode.PROXY) {
  7.                 // 注册InfrastructureAdvisorAutoProxyCreator组件
  8.                 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
  9.                 if ((Boolean) proxyTargetClass) {
  10.                         // 设置proxyTargetClass为true
  11.                         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  12.                         return;
  13.                 }
  14.         }
  15. }
复制代码
AutoProxyRegistrar会向容器注册InfrastructureAdvisorAutoProxyCreator组件,InfrastructureAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator,用于创建Advisor代理,但是它只考虑基础架构Advisor bean,会忽略@Aspect组件。
若需要支持@Aspect组件,需要使用@EnableAspectJAutoProxy注解开启AspectJ支持,EnableAspectJAutoProxy注解会注册AnnotationAwareAspectJAutoProxyCreator组件,AnnotationAwareAspectJAutoProxyCreator也继承了AbstractAutoProxyCreator,支持Advisor和@Aspect组件。这个内容在之前的AOP源码分析中记录过,此处不再展开分析。
参考AOP源码分析:
https://blog.csdn.net/xuguofeng2016/article/details/128114972
AnnotationAwareAspectJAutoProxyCreator的优先级比InfrastructureAdvisorAutoProxyCreator高,所以当同时注册时,会使用AnnotationAwareAspectJAutoProxyCreator作为Advisor代理创建器。
继承关系:

ProxyTransactionManagementConfiguration

注入事务通知相关组件:

  • BeanFactoryTransactionAttributeSourceAdvisor - 实现了PointcutAdvisor接口
  • TransactionInterceptor - 实现了MethodInterceptor接口
Advisor、Advice、Pointcut是spring aop的三组件,aop中已重点分析过,此处不再记录。
ASPECTJ模式

如果EnableTransactionManagement注解的mode属性设置为ASPECTJ模式,会导入AspectJTransactionManagementConfiguration组件。
该模式需要依赖spring-aspects模块。
AspectJTransactionManagementConfiguration

org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration
@Configuration class that registers the Spring infrastructure beans necessary to enable AspectJ-based annotation-driven transaction management for Spring's own org.springframework.transaction.annotation.Transactional annotation.
这个类会装配AnnotationTransactionAspect对象,AnnotationTransactionAspect是一个原生AspectJ组件,该组件使用原生AspectJ在类加载阶段为目标方法嵌入事务拦截逻辑以实现事务管理。
在启动时需要添加以下参数:
  1. -javaagent:path/to/aspectjweaver-${version}.jar
复制代码
AspectJ参考资料

https://javadoop.com/post/aspectj
https://www.eclipse.org/aspectj/docs.php

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

乌市泽哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表