Spring 源码阅读 73:@EnableTransactionManagement 分析

1,657 阅读6分钟

基于 Spring Framework v5.2.6.RELEASE

概述

开发 Spring 应用时,通过在配置类上添加 @EnableTransactionManagement 注解,可以开启开启注解驱动的事务管理能力,被 @Transactional 注解标注的类或者方法,将会在一个数据库事务中执行。

本文将从 @EnableTransactionManagement 注解的源码出发,分析它是如何开启事务管理的。

EnableTransactionManagement 分析

进入 EnableTransactionManagement 的源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

boolean proxyTargetClass() default false;

AdviceMode mode() default AdviceMode.PROXY;

int order() default Ordered.LOWEST_PRECEDENCE;

}

@EnableTransactionManagement 注解包含三个属性。

proxyTargetClass 属性

proxyTargetClass属性表示是否强制采用 CGLIB 的方式来创建代理对象。在 Spring 中,事务管理是通过 AOP 特性来实现的,而 AOP 是通过对 Bean 的代理来实现的,包括 JDK 动态代理和 CGLIB 代理,默认情况下 Spring 会优先采用 JDK 动态代理的方式,在无法采用 JDK 动态代理的时候,则采用 CGLIB 的方式。

默认情况下,proxyTargetClass的值为false,如果将其配置为true,则所有的代理对象都会通过 CGLIB 的方式来创建。

值得注意的是,如果这里将proxyTargetClass配置为true,那么,不仅被标记了 @Transactional 注解的类以及其标记的方法所在的类会被强制采用 CGLIB 创建代理对象,Spring 容器中所有需要创建代理的 Bean 都会被强制采用 CGLIB 的方式来创建代理。

mode 属性

mode的值是一个 AdviceMode 类型的枚举,默认值是AdviceMode.PROXY。它表示只能通过代理模式执行拦截的逻辑,这是通常情况下会采用的值。

order 属性

order属性表示事务相关的增强的顺序,默认是Ordered.LOWEST_PRECEDENCE,也就是最小值。

TransactionManagementConfigurationSelector 分析

除了以上三个配置属性外,@EnableTransactionManagement 通过 @Import 元注解,倒入了配置类型 TransactionManagementConfigurationSelector。

先看它的继承关系。

它是一个 ImportSelector 的实现类,先介绍一下实现了 ImportSelector 的类型,是如何在 Spring 的配置中起作用的。

当 Spring 扫描到应用中的某个配置类上被标记了 @EnableTransactionManagement 注解时,会通过它的 @Import 元注解找到其导入的类型 TransactionManagementConfigurationSelector,然后,判断出它是 ImportSelector 的实现类,就会调用它的selectImports方法,执行其中的配置逻辑。

此处被调用的是参数类型为 AnnotationMetadata 的selectImports方法,被调用时传入的参数封装了 @EnableTransactionManagement 注解的元信息。但是,TransactionManagementConfigurationSelector 并没有实现这个方法,因此,它继承了父类 AdviceModeImportSelector 中该方法的实现。

// org.springframework.context.annotation.AdviceModeImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
   Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
   Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");

   AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
   if (attributes == null) {
      throw new IllegalArgumentException(String.format(
            "@%s is not present on importing class '%s' as expected",
            annType.getSimpleName(), importingClassMetadata.getClassName()));
   }

   AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
   String[] imports = selectImports(adviceMode);
   if (imports == null) {
      throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
   }
   return imports;
}

其中比较核心的逻辑就是从注解元信息中,获取到mode属性的值,然后将其作为参数调用了另外一个selectImports方法,这是一个模版方法,可以在 TransactionManagementConfigurationSelector 中找到方法的实现。

// org.springframework.transaction.annotation.TransactionManagementConfigurationSelector#selectImports
@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;
   }
}

在默认情况下,也就是switch语句的PROXY分支,会返回 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 的类名称。

前面交代了,这个方法是在 Spring 扫描和处理配置类的时候被调用的,当返回这两个类名称后,Spring 会再解析这里两个类。

接下来我们再分别来看这两个类。

AutoProxyRegistrar 分析

AutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,因此,当 Spring 加载到它之后,会执行其中的registerBeanDefinitions方法,执行一些注册 BeanDefinition 的逻辑。

@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) {
            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            if ((Boolean) proxyTargetClass) {
               AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
               return;
            }
         }
      }
   }
   // 此处省略 LOG 代码
}

这个方法中,依然可以获取到 @EnableTransactionManagement 的注解元数据,通过从中获取modeproxyTargetClass属性的值,执行后续的操作。

mode的值是AdviceMode.PROXY的时候,会执行 AopConfigUtils 的registerAutoProxyCreatorIfNecessary方法。从这个方法名可以看出,它的作用是创建用于给 Bean 创建代理对象的后处理器,因为 Spring 中的事务管理是依赖 AOP 特性的,因此,开启事务管理之前要先确保开启的 AOP 功能。

如果proxyTargetClass的值是true的话,还会再额外执行 AopConfigUtils 的forceAutoProxyCreatorToUseClassProxying方法,它的作用是找到 Spring 容器中用来创建 AOP 代理对象的后处理器的 BeanDefinition,并为其添加proxyTargetClass属性,值为true,这样,在为 Bean 实例创建代理对象时,就会直接采用 CGLIB 的方式。

总结,AutoProxyRegistrar 中的配置逻辑,其实就是为了确保 Spring 在开启事务管理之前,开启了 AOP 特性。

ProxyTransactionManagementConfiguration 分析

ProxyTransactionManagementConfiguration 是一个标记了 @Configuration 注解的配置类,Spring 解析它的时候,会获取到 @Bean 标记的方法,并在容器中注册对应的 BeanDefinition。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
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;
   }

}

在这个配置类中,通过 @Bean 方法,注册了三个 Bean,其中 BeanFactoryTransactionAttributeSourceAdvisor 依赖另外两者,也是三者中扮演重要角色的一个。从它的名字就可以看出,它是一个 Advisor 实现类,也意味着事务管理的相关增强逻辑、增强逻辑与类和方法的匹配,都是通过它来执行的。

AbstractTransactionManagementConfiguration 分析

除此之外,ProxyTransactionManagementConfiguration 的父类 AbstractTransactionManagementConfiguration 同样是一个 @Configuration 注解标记的配置类,其中有两个配置相关的方法。

@Autowired(required = false)
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
   if (CollectionUtils.isEmpty(configurers)) {
      return;
   }
   if (configurers.size() > 1) {
      throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
   }
   TransactionManagementConfigurer configurer = configurers.iterator().next();
   this.txManager = configurer.annotationDrivenTransactionManager();
}

setConfigurers方法是一个被标记了 @Autowired 注解的方法,参数重传入了 TransactionManagementConfigurer 类型的集合configurers。在方法体中,如果集合为空,则不做任何处理,如果集合元素数大于 1,则抛出异常,最后,调用集合中唯一的元素的annotationDrivenTransactionManager方法,得到一个 TransactionManager 对象, 赋值给成员变量txManager

从上述的逻辑中可以看出,这个方法用来加载 TransactionManager 配置。因此,我们可以创建一个类,通过实现 TransactionManagementConfigurer 接口及其 annotationDrivenTransactionManager 方法,来通过代码配置 TransactionManager。只需将这个类型作为一个 Bean 注册到 Spring 容器中,即可在此处被加载到。

@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
   return new TransactionalEventListenerFactory();
}

最后,还通过 @Bean 方法的方式,注册了一个 TransactionalEventListenerFactory 类型的 Bean。

总结

到这里,我们就知道了 @EnableTransactionManagement 是如何开启事务管理的。首先,它提供了几个配置属性,并引入了 TransactionManagementConfigurationSelector 配置类;然后,在 TransactionManagementConfigurationSelector 中,又引入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,它们两者的作用是确保 Spring 已经开启了 AOP 特性以及向容器中注册用于处理事务管理的 Advisor。


本文正在参加「金石计划 . 瓜分6万现金大奖」