你认真研究过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;
    }

}

发表评论