【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.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<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;
}
}