你认真研究过Spring中的@EnableTransactionManagement注解吗?

【1】基本功能

简单来讲, @EnableTransactionManagement注解就是在SpringBoot环境(或者JavaConfig)下开启事务管理。与<tx:annotation-driven/> 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配置如下:

 <beans>

     <tx:annotation-driven/>

     <bean id="fooRepository" class="com.foo.JdbcFooRepository">
         <constructor-arg ref="dataSource"/>
     </bean>

     <bean id="dataSource" class="com.vendor.VendorDataSource"/>

     <bean id="transactionManager" class="org.sfwk...DataSourceTransactionManager">
         <constructor-arg ref="dataSource"/>
     </bean>

 </beans>

这两种常见细微不同处在于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.PROXYAdviceMode.ASPECTJ

  • AdviceMode.PROXY 也就是代理模式只能通过代理调用拦截实现通知增强,本地调用不能实现通知增强;
  • AdviceMode.ASPECTJ 这种情况下proxyTargetClass属性将会被忽略,其将通过编译时织入或者加载时织入对目标类进行切面包装。这种情况下不涉及代理,本地调用也同样生效。需要注意,这种情况下需要引入 spring-aspects 模块 jar包。

【2】注解引入

我们继续分析这个注解引入了什么。如下所示,其通过@Import注解引入了TransactionManagementConfigurationSelector。

@Import(TransactionManagementConfigurationSelector.class)

这个TransactionManagementConfigurationSelector是什么呢?如下所示其实现了ImportSelector接口。其提供了selectImports用来选择导入某些符合条件的配置类。
在这里插入图片描述

我们看下其selectImports如下所示,当adviceMode为PROXY时 ,其将会导入AutoProxyRegistrarProxyTransactionManagementConfiguration。当adviceMode为ASPECTJ时,会判断是否为JTA环境进行不同引入。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@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<String> 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.<Integer>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;
	}

}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值