首页 > 其他分享 >Spring声明式事务的工作原理

Spring声明式事务的工作原理

时间:2023-02-08 18:15:06浏览次数:35  
标签:事务 return Spring class public txAttr null 声明 method

Spring事务管理Java后端项目开发中都会用到的一个技术点,每个Java工程师都必须精通。

Spring事务管理可以分为两类:

  • 声明式事务管理
  • 编程式事务管理

声明式事务管理只需要在代码中添加@Transactional注解,即可自动进行事务管理。由于使用方便,是项目开发中的首选。

在Spring Boot中,只要我们引入了相关依赖,就会自动开启声明式事务功能。

例如,我们引入mybatis-spring-boot-starter,它会自动引入spring-tx等相关依赖,并且自动开启事务功能:

<dependency>  
   <groupId>org.mybatis.spring.boot</groupId>  
   <artifactId>mybatis-spring-boot-starter</artifactId>  
</dependency>

Spring Boot的声明式事务的工作原理如上图,总的来说经过三个步骤:

  1. TransactionAutoConfiguration为核心,基于Spring Boot自动配置机制,创建事务相关bean。对于声明式事务来说,它会开启@EnableTransactionManagement注解。
  2. @EnableTransactionManager会使用@Import注解引入TransactionManagementConfigurationSelector,然后开启基于代理或Aspectj的声明式事务功能。
  3. 对于基于代理的声明式事务,ProxyTransactionManagementConfiguration会创建基于@Transactional注解的Advisor。在bean实例化过程中,AOP功能会根据该Advisor对相关bean进行代理。

1 TransactionAutoConfiguration

TransactionAutoConfiguration会对事务功能进行自动配置,与声明式事务相关的源码如下:

@AutoConfiguration  
@ConditionalOnClass(PlatformTransactionManager.class)  
@EnableConfigurationProperties(TransactionProperties.class)  
public class TransactionAutoConfiguration {  
   @Configuration(proxyBeanMethods = false)  
   @ConditionalOnBean(TransactionManager.class)  
   @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)  
   public static class EnableTransactionManagementConfiguration {  
  
      @Configuration(proxyBeanMethods = false)  
      @EnableTransactionManagement(proxyTargetClass = false)  
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")  
      public static class JdkDynamicAutoProxyConfiguration {  
      }  
  
      @Configuration(proxyBeanMethods = false)  
      @EnableTransactionManagement(proxyTargetClass = true)  
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",  
            matchIfMissing = true)  
      public static class CglibAutoProxyConfiguration {  
      }  
   }  
}

首先,TransactionAutoConfiguration标注了三个注解:

  • @AutoConfiguration:表示TransactionAutoConfiguration可以作为配置类被Spring Boot自动配置。
  • @ConditionalOnClass(PlatformTransactionManager.class):表示只有存在PlatformTransactionManager类时,Spring容器才会注册TransactionAutoConfiguration
  • @EnableConfigurationProperties(TransactionProperties.class):表示读取配置文件中的spring.transaction属性。

然后,根据配置文件中的spring.aop属性,会分别引入JdkDynamicAutoProxyConfiguration或CglibAutoProxyConfiguration(默认)配置类。

这两个配置类都是空的,它们的作用其实是标注@EnableTransactionManagement注解。

2 @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;  
}

它的作用是通过@Import注解引入TransactionManagementConfigurationSelector,然后根据AdviceMode分别开启基于proxyaspectj的声明式事务管理功能(注册对应的配置类):

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {  
   protected String[] selectImports(AdviceMode 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);  
   }  
  
}

3 ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration的核心作用就是创建声明式事务管理的Advisor,AOP会根据它对bean进行动态代理:

@Configuration(proxyBeanMethods = false)  
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)  
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {  
   // 注册Advisor
   @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;  
   }  
  
   // 注册@Transactional的pointcut解析数据源
   @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;  
   }  
}

3.1 @Transactional解析器

AnnotationTransactionAttributeSource中定义了对@Transactional等注解的解析规则:

public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {  
   this.publicMethodsOnly = publicMethodsOnly;  
   if (jta12Present || ejb3Present) {  
      this.annotationParsers = new LinkedHashSet<>(4);  
      this.annotationParsers.add(new SpringTransactionAnnotationParser());  
      if (jta12Present) {  
         this.annotationParsers.add(new JtaTransactionAnnotationParser());  
      }  
      if (ejb3Present) {  
         this.annotationParsers.add(new Ejb3TransactionAnnotationParser());  
      }  
   }  
   else {  
      this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());  
   }  
}

例如,SpringTransactionAnnotationParser可以判断bean是否标注@Transactional注解:

public boolean isCandidateClass(Class<?> targetClass) {  
   return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);  
}

也可以获取@Transactional的属性信息:

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {  
   RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();  
  
   Propagation propagation = attributes.getEnum("propagation");  
   rbta.setPropagationBehavior(propagation.value());  
   Isolation isolation = attributes.getEnum("isolation");  
   rbta.setIsolationLevel(isolation.value());  
  
   rbta.setTimeout(attributes.getNumber("timeout").intValue());  
   String timeoutString = attributes.getString("timeoutString");  
   Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,  
         "Specify 'timeout' or 'timeoutString', not both");  
   rbta.setTimeoutString(timeoutString);  
  
   rbta.setReadOnly(attributes.getBoolean("readOnly"));  
   rbta.setQualifier(attributes.getString("value"));  
   rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));  
  
   List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();  
   for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {  
      rollbackRules.add(new RollbackRuleAttribute(rbRule));  
   }  
   for (String rbRule : attributes.getStringArray("rollbackForClassName")) {  
      rollbackRules.add(new RollbackRuleAttribute(rbRule));  
   }  
   for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {  
      rollbackRules.add(new NoRollbackRuleAttribute(rbRule));  
   }  
   for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {  
      rollbackRules.add(new NoRollbackRuleAttribute(rbRule));  
   }  
   rbta.setRollbackRules(rollbackRules);  
  
   return rbta;  
}

3.2 事务方法拦截器

TransactionInterceptor中定义了事务方法的执行逻辑,TransactionInterceptor#invoke()

public Object invoke(MethodInvocation invocation) throws Throwable {  
   // Work out the target class: may be {@code null}.  
   // The TransactionAttributeSource should be passed the target class   
   // as well as the method, which may be from an interface.   
   Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);  
  
   // Adapt to TransactionAspectSupport's invokeWithinTransaction...  
   return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {  
      @Override  
      @Nullable      
      public Object proceedWithInvocation() throws Throwable {  
         return invocation.proceed();  
      }  
      @Override  
      public Object getTarget() {  
         return invocation.getThis();  
      }  
      @Override  
      public Object[] getArguments() {  
         return invocation.getArguments();  
      }  
   });  
}

TransactionAspectSupport#invokeWithinTransaction()中会根据@Transactional注解信息执行创建事务/加入事务,执行业务方法,提交事务/回滚等流程:

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,  
      final InvocationCallback invocation) throws Throwable {  
  
   // 获取事务属性信息,例如@Transactional注解的属性  
   TransactionAttributeSource tas = getTransactionAttributeSource();  
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);  
   
   // 获取事务管理器
   final TransactionManager tm = determineTransactionManager(txAttr);  
  
   // ReactiveTransactionManager事务管理器执行流程(一般跳过)
   if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {  
      // 省略……
   }  
  
   // PlatformTransactionManager事务管理器执行流程
   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);  
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);  
  
   // DataSourceTransactionManager或JtaTransactionManager等事务管理器执行流程
   if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {  
      // 获取事务信息:根据@Transactional注解信息创建事务/加入事务等 
      TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);  
  
      Object retVal;  
      try {  
         // 执行业务方法
         retVal = invocation.proceedWithInvocation();  
      }  
      catch (Throwable ex) {  
         // 业务方法抛异常,根据@Transactional注解信息进行回滚或提交
         completeTransactionAfterThrowing(txInfo, ex);  
         throw ex;  
      }  
      finally {  
         // 重置当前线程的事务信息
         cleanupTransactionInfo(txInfo);  
      }  
  
      if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {  
         // Set rollback-only in case of Vavr failure matching our rollback rules...  
         TransactionStatus status = txInfo.getTransactionStatus();  
         if (status != null && txAttr != null) {  
            retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);  
         }  
      }  
  
      // 如果当前事务仍存在,提交事务
      commitTransactionAfterReturning(txInfo);  
      return retVal;  
   }  
  
   // CallbackPreferringPlatformTransactionManager(WebSphereUowTransactionManager)事务管理器执行流程(一般跳过)
   else {  
      // 省略……
   }  
}

3.3 BeanFactoryTransactionAttributeSourceAdvisor

BeanFactoryTransactionAttributeSourceAdvisor定义了声明式事务管理的切面信息,核心包括:

  • 判断bean对象是否需要进行声明式事务代理:pointcuttransactionAttributeSourceAnnotationTransactionAttributeSource)。
  • 声明式事务的拦截逻辑:advice(具体逻辑查看TransactionInterceptor

首先,通过TransactionAttributeSourcePointcut#matches()可以判断是否需要进行声明式事务代理:

public boolean matches(Method method, Class<?> targetClass) {  
   // AnnotationTransactionAttributeSource对象
   TransactionAttributeSource tas = getTransactionAttributeSource();  
   // 解析@Transactional注解信息
   return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);  
}

通过AbstractFallbackTransactionAttributeSource#getTransactionAttribute()方法解析@Transactional注解信息:

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {  
   if (method.getDeclaringClass() == Object.class) {  
      return null;  
   }  
  
   // 获取缓存
   Object cacheKey = getCacheKey(method, targetClass);  
   TransactionAttribute cached = this.attributeCache.get(cacheKey);  
   if (cached != null) {  
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {  
         return null;  
      }  
      else {  
         return cached;  
      }  
   }  
   // 解析,并添加到缓存
   else {  
      // We need to work it out.  
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);  
      // Put it in the cache.  
      if (txAttr == null) {  
         this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);  
      }  
      else {  
         String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);  
         if (txAttr instanceof DefaultTransactionAttribute) {  
            DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;  
            dta.setDescriptor(methodIdentification);  
            dta.resolveAttributeStrings(this.embeddedValueResolver);  
         }   
         this.attributeCache.put(cacheKey, txAttr);  
      }  
      return txAttr;  
   }  
}

AbstractFallbackTransactionAttributeSource#computeTransactionAttribute()

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {  
   // Don't allow non-public methods, as configured.  
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {  
      return null;  
   }  
  
   // The method may be on an interface, but we need attributes from the target class.  
   // If the target class is null, the method will be unchanged.   
   Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);  
  
   // 解析方法中的@Transactional信息,会调用SpringTransactionAnnotationParser#parseTransactionAnnotation()方法
   TransactionAttribute txAttr = findTransactionAttribute(specificMethod);  
   if (txAttr != null) {  
      return txAttr;  
   }  
  
   // 解析类中的@Transactinal信息,会调用SpringTransactionAnnotationParser#parseTransactionAnnotation()方法
   txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());  
   if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {  
      return txAttr;  
   }  
  
   // 对method/method.getDeclaringClass()进行解析
   if (specificMethod != method) {  
      // Fallback is to look at the original method.  
      txAttr = findTransactionAttribute(method);  
      if (txAttr != null) {  
         return txAttr;  
      }  
      // Last fallback is the class of the original method.  
      txAttr = findTransactionAttribute(method.getDeclaringClass());  
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {  
         return txAttr;  
      }  
   }  
   return null;  
}

标签:事务,return,Spring,class,public,txAttr,null,声明,method
From: https://www.cnblogs.com/Xianhuii/p/17102828.html

相关文章

  • springboot 动态获取配置信息完成启动
    架构说设计到数据量较大的应用要从k8s中迁出单独机器部署于是将8节点的服务准备迁出,且端口号在数据库中保存在不引入springcloud的方式下启动spring容器中对args进行配......
  • Spring30 - Resource资源读取
    资源操作:Resources8.1、SpringResources概述Java的标准java.net.URL类和各种URL前缀的标准处理程序无法满足所有对low-level资源的访问,比如:没有标准化的URL实现可用......
  • SpringBoot工程入门case
    SpringBoot的设计目的是用来简化Spring应用的初始搭建以及开发过程。SpringBoot入门案例:1、创建一个新module  2、除pom和src文件剩余都删除。  3、在src.com......
  • springboot开发日记(7)
    springboot——自动配置在日记(2)中提到过,@SpringBootApplication由以下三个注解组合而成:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan1.@Spr......
  • SpringBoot
    SpringBoot文章来源于:雷神:https://www.bilibili.com/video/BV19K4y1L7MT/?spm_id_from=333.337.search-card.all.click&vd_source=a9bff059910348f08db3690eefbeacbe特点......
  • 【Spring-boot-route(二)读取配置文件的几种方式】
    SpringBoot提供了两种格式的配置文件,分别是​​properties​​​和​​yml​​。SpringBoot最大的特点就是自动化配置,如果我们想修改自动化配置的默认值,就可以通过配置......
  • 分布式系统02—分布式事务解决方案
    二阶段提交二阶段提交(Two-phaseCommit)是指,在计算机网络以及数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。......
  • Spring28 - 全注解实现事务管理
    全注解配置事务​核心思想:使用配置类替代配置文件①添加配置类packagecom.atguigu.spring6.config;importcom.alibaba.druid.pool.DruidDataSource;importor......
  • Spring27 - 基于注解的事务管理
    没有事务时遇到的问题模拟场景用户购买图书,先查询图书的价格,再更新图书的库存和用户的余额假设用户id为1的用户,购买id为1的图书用户余额为50,而图书价格为80购买图书之......
  • Spring29 - 基于XML的事务管理
    基于XML的声明式事务场景模拟参考基于注解的声明式事务与注解管理事务相同的通用步骤(1)开启组件扫描<!--扫描组件--><context:component-scanbase-package="com.......