你认真研究过Spring中的@EnableTransactionManagement注解吗?
【1】基本功能
简单来讲, @EnableTransactionManagement注解就是在SpringBoot环境(或者JavaConfig)下开启事务管理。与 xml配置类似用来注入Spring支持注解驱动的事务管理行为时必须的组件,比如TransactionInterceptor。
① 应用场景
比如JavaConfig下应用如下:
@Configuration @EnableTransactionManagement public class AppConfig { @Bean public FooRepository fooRepository() { // configure and return a class having @Transactional methods return new JdbcFooRepository(dataSource()); } @Bean public DataSource dataSource() { // configure and return the necessary JDBC DataSource } @Bean public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dataSource()); } }
等同于xml配置如下:
这两种常见细微不同处在于xml配置将会默认根据beanName=transactionManager获取事务管理器,而JavaConfig示例中默认根据类型PlatformTransactionManager 查找,因此名字可以是”txManager”, “transactionManager”, or “tm”:,它根本不重要。
对于那些希望在@EnableTransactionManagement和要使用的确切事务管理器bean之间建立更直接关系的人,可以实现TransactionManagementConfigurer回调接口:
@Configuration @EnableTransactionManagement public class AppConfig implements TransactionManagementConfigurer { @Bean public FooRepository fooRepository() { // configure and return a class having @Transactional methods return new JdbcFooRepository(dataSource()); } @Bean public DataSource dataSource() { // configure and return the necessary JDBC DataSource } @Bean public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dataSource()); } //用于处理@Transactional方法 @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return txManager(); } }
② 注解源码
我们再来看一下注解源码。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { // true - subclass-based (CGLIB) proxies;false - Java interface-based proxies boolean proxyTargetClass() default false; // 标明事务通知如何应用,默认是代理方式 AdviceMode mode() default AdviceMode.PROXY; // 标明transaction advisor的执行顺序,当有多个Advice 应用在同一个joinpoint时 int order() default Ordered.LOWEST_PRECEDENCE; }
这里首先需要说明一点,理论上当proxyTargetClass = true时,无论target是否实现了接口,其都是CGLIB代理。当proxyTargetClass =false时,实现了接口的使用JDK代理,未实现接口的使用CGLIB代理。
但是这种情况在SpringBoot2.X后发生了改变,其AopAutoConfiguration中的CglibAutoProxyConfiguration默认修改proxyTargetClass为true。此时你再通过@EnableAspectJAutoProxy(proxyTargetClass = false)来尝试控制行为将不生效。
可以使用spring.aop.proxy-target-class=false来控制这种情况。
我们再看下其mode属性。该属性控制Advice如何被应用,有两种情况:AdviceMode.PROXY和AdviceMode.ASPECTJ。
AdviceMode.PROXY 也就是代理模式只能通过代理调用拦截实现通知增强,本地调用不能实现通知增强;
AdviceMode.ASPECTJ 这种情况下proxyTargetClass属性将会被忽略,其将通过编译时织入或者加载时织入对目标类进行切面包装。这种情况下不涉及代理,本地调用也同样生效。需要注意,这种情况下需要引入 spring-aspects 模块 jar包。
【2】注解引入
我们继续分析这个注解引入了什么。如下所示,其通过@Import注解引入了TransactionManagementConfigurationSelector。
@Import(TransactionManagementConfigurationSelector.class)
这个TransactionManagementConfigurationSelector
是什么呢?如下所示其实现了ImportSelector
接口。其提供了selectImports用来选择导入某些符合条件的配置类。
我们看下其selectImports如下所示,当adviceMode为PROXY时 ,其将会导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration。当adviceMode为ASPECTJ时,会判断是否为JTA环境进行不同引入。
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } } private String determineTransactionAspectClass() { return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); } } //The name of the AspectJ transaction management @{@code Configuration} class for JTA. public static final String JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.transaction.aspectj.AspectJJtaTransactionManagementConfiguration"; //The name of the AspectJ transaction management @{@code Configuration} class. public static final String TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
我们继续看AutoProxyRegistrar和ProxyTransactionManagementConfiguration做了什么。
① AutoProxyRegistrar
其将会注册auto proxy creator(APC) 并根据注解@Enable*的值设置mode和proxyTargetClass属性为正确的值。当proxyTargetClass为true时,APC强制使用CGLIB代理。
这里需要说明的是,不止TransactionManagementConfigurationSelector 引入了AutoProxyRegistrar。@EnableAspectJAutoProxy注解也引入了AutoProxyRegistrar并提供了proxyTargetClass属性。所以AutoProxyRegistrar根本不关心究竟是谁提供了mode和proxyTargetClass属性,其大多数都共享了AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME=org.springframework.aop.config.internalAutoProxyCreator 这个单例APC(实际是AnnotationAwareAspectJAutoProxyCreator)。
因此,这个AutoProxyRegistrar
并不“关心”它找到的是哪个注解——只要它公开正确的mode和proxyTargetClass属性,就可以注册和配置APC。
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set annTypes = importingClassMetadata.getAnnotationTypes(); for (String annType : annTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType); if (candidate == null) { continue; } Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); //如果注解属性校验通过 if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) { // 注册internalAutoProxyCreator AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); // 如果为true,则强制使用CGLIB代理 if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } //... }
总结来讲就是这个注册器会尝试向容器中注册internalAutoProxyCreator
这个单例Bean并尝试设置属性值proxyTargetClass为true。
② ProxyTransactionManagementConfiguration
这是一个事务管理配置类,注册了Advisor、Advice以及transactionAttributeSource。这里Advice其实就是transactionInterceptor。用在什么地方呢?Advisor会为我们应用了事务的service进行代理包装,当进行数据库操作时(比如插入一行记录)就会触发代理的行为。其实Spring事务的核心原理就在这里,就是代理控制的。
@Configuration(proxyBeanMethods = false) public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource); advisor.setAdvice(transactionInterceptor); if (this.enableTx != null) { advisor.setOrder(this.enableTx.getNumber("order")); } return advisor; } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor( TransactionAttributeSource transactionAttributeSource) { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
发表评论