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

简介: 你认真研究过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.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;
  }
}
目录
相关文章
|
1月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
49 0
|
2月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
121 3
|
19天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
35 4
|
1月前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
60 4
SpringBoot必须掌握的常用注解!
|
19天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
59 2
|
19天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
40 2
|
22天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
35 5
|
22天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
26 2
|
23天前
|
JSON Java 数据格式
springboot常用注解
@RestController :修饰类,该控制器会返回Json数据 @RequestMapping(“/path”) :修饰类,该控制器的请求路径 @Autowired : 修饰属性,按照类型进行依赖注入 @PathVariable : 修饰参数,将路径值映射到参数上 @ResponseBody :修饰方法,该方法会返回Json数据 @RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中 @Controller@Service@Compont: 将类注册到ioc容器
|
23天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
36 2