SpringBoot源码解读与原理分析(三十三)SpringBoot整合JDBC(二)声明式事务的生效原理和控制流程

文章目录

    • 前言
    • 10.3 声明式事务的生效原理
      • 10.3.1 TransactionAutoConfiguration
      • 10.3.2 TransactionManagementConfigurationSelector
      • 10.3.3 AutoProxyRegistrar
      • 10.3.4 InfrastructureAdvisorAutoProxyCreator
      • 10.3.5 ProxyTransactionManagementConfiguration
        • 10.3.5.1 TransactionAttributeSource
        • 10.3.5.2 TransactionInterceptor
        • 10.3.5.3 BeanFactoryTransactionAttributeSourceAdvisor
    • 10.4 声明式事务的控制流程
      • 10.4.1 CglibAopProxy#intercept()
      • 10.4.2 TransactionInterceptor#invoke()
        • 10.4.2.1 获取TransactionAttribute
        • 10.4.2.2 获取TransactionManager
        • 10.4.2.3 响应式事务管理器的处理
        • 10.4.2.4 事务控制的核心
          • (1)成功的事务提交
          • (2)异常的事务回滚
        • 10.4.2.5 事务执行的后处理

前言

SpringBoot整合JDBC的场景中,除了引入spring-jdbc,还会引入spring-tx实现事务控制。

在 SpringBoot源码解读与原理分析(三十二)SpringBoot整合JDBC(一)JDBC组件的自动装配 的示例项目中,在主启动类显式标注了@EnableTransactionManagement注解,用于开启注解声明式事务。但实际上,即便不进行标注,底层仍然会使用自动配置类的方式开启,也就是说SpringBoot默认开启注解声明式事务

具体的开启位置在自动配置类TransactionAutoConfiguration中。

10.3 声明式事务的生效原理

10.3.1 TransactionAutoConfiguration

源码1TransactionAutoConfiguration.java@Configuration(proxyBeanMethods = false)
// ......
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",matchIfMissing = 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 {}}
}

由 源码1 可知,即使没有显式标注配置类@EnableTransactionManagement,底层的配置类EnableTransactionManagementConfiguration中也会进行开启。有些许不同的是,这里会根据项目中配置的AOP是否代理目标对象(proxyTargetClass的值)来决定创建哪种事务代理对象。

既然注解声明式事务的最终开关是@EnableTransactionManagement注解,那么这个注解的内部一定使用@Import注解导入了一些特殊的组件。

源码2EnableTransactionManagement.java@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {boolean proxyTargetClass() default false;AdviceMode mode() default AdviceMode.PROXY;int order() default Ordered.LOWEST_PRECEDENCE;
}

由 源码2 可知,@EnableTransactionManagement注解通过@Import注解导入了一个TransactionManagementConfigurationSelector,并且包含三个属性。

  • proxyTargetClass:该属性为true时,创建基于子类的代理(使用Cglib);该属性默认为false,即创建基于标准Java接口的代理。
  • order:优先级,默认为Ordered.LOWEST_PRECEDENCE。
  • mode:事务通知应用的模式。该属性的默认值为AdviceMode.PROXY,表示事务通知会在程序运行期间使用动态代理的方式向目标对象织入;该属性的另一个取值是AdviceMode.ASPECTJ,表示事务通知会在类加载期间向目标对象织入。

10.3.2 TransactionManagementConfigurationSelector

由类名可知,该组件是一个ImportSelector。

源码3TransactionManagementConfigurationSelector.javapublic class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {@Overrideprotected 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;}}
}

由 源码3 可知,该组件的selectImports方法会根据@EnableTransactionManagement注解的mode属性的值决定导入哪些组件。

当mode=PROXY时,导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration两个组件。

10.3.3 AutoProxyRegistrar

源码4AutoProxyRegistrar.javapublic class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound = false;Set<String> annTypes = importingClassMetadata.getAnnotationTypes();for (String annType : annTypes) {// 遍历类上标注的所有注解// 找到@EnableTransactionManagement注解AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);if (candidate == null) {continue;}// 获取@EnableTransactionManagement注解的mode属性和proxyTargetClass属性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) {// 当mode属性的值为PROXY,注册一个InfrastructureAdvisorAutoProxyCreator组件AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean) proxyTargetClass) {// 当proxyTargetClass属性的值为true,强制AutoProxyCreator使用类代理AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}// ......}
}
源码5AopConfigUtils.javapublic static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {return registerAutoProxyCreatorIfNecessary(registry, null);
}public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

由 源码4-5 可知,AutoProxyRegistrar本身是一个ImportBeanDefinitionRegistrar,它的作用是向BeanDefinitionRegistrar中注册新的BeanDefinition。

从核心方法registerBeanDefinitions可以看出,AutoProxyRegistrar会根据@EnableTransactionManagement注解的mode属性和proxyTargetClass属性的值决定是否注册特定的组件。

默认情况下,mode属性的值为PROXY,因此registerBeanDefinitions方法会借助AopConfigUtils类注册一个InfrastructureAdvisorAutoProxyCreator组件。

在mode属性的值为PROXY前提下,如果proxyTargetClass属性的值为true,则会强制AutoProxyCreator使用类代理。

10.3.4 InfrastructureAdvisorAutoProxyCreator

源码6InfrastructureAdvisorAutoProxyCreator.javapublic class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator

由 源码6 可知,InfrastructureAdvisorAutoProxyCreator与AOP代理对象创建器AnnotationAwareAspectJAutoProxyCreator类似(详见 SpringBoot源码解读与原理分析(二十八)AOP模块的生命周期(一) 9.2 AnnotationAwareAspectJAutoProxyCreator),它们都继承了AbstractAdvisorAutoProxyCreator类,因此它们都可以创建代理对象。

查阅InfrastructureAdvisorAutoProxyCreator类的javadoc:

Auto-proxy creator that considers infrastructure Advisor beans only, ignoring any application-defined Advisors.
自动代理创建器只考虑基础类型的增强器,忽略任何应用程序自定义的增强器。

什么是“基础类型”?实际上是BeanDefinition中给Bean定义的3种角色:

源码7BeanDefinition.javaint ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;

“基础类型”指的是BeanDefinition中的角色为ROLE_INFRASTRUCTURE。通常,只有SpringFramework内部定义的Bean才可能被标注为ROLE_INFRASTRUCTURE角色,而且这些Bean在应用程序中起到基础支撑的作用。

因此,可以得出以下结论:事务控制的核心是AOP中的一个MethodInterceptor,它的角色刚好是ROLE_INFRASTRUCTURE。InfrastructureAdvisorAutoProxyCreator在bean对象的初始化期间找到这个MethodInterceptor并包装为Advisor,给需要进行注解事务控制的bean对象构造代理对象。

值得注意的是,InfrastructureAdvisorAutoProxyCreator与AnnotationAwareAspectJAutoProxyCreator不会同时注册。由于AnnotationAwareAspectJAutoProxyCreator可以处理所有角色的通知,因此它的优先级更高,如果先注册了AnnotationAwareAspectJAutoProxyCreator,则不会再注册InfrastructureAdvisorAutoProxyCreator。

10.3.5 ProxyTransactionManagementConfiguration

TransactionManagementConfigurationSelector导入的另一个组件是ProxyTransactionManagementConfiguration配置类,其内部注册了3个与事务控制相关的核心组件。

10.3.5.1 TransactionAttributeSource
源码8ProxyTransactionManagementConfiguration.java@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();
}
源码9TransactionAttributeSource.javapublic interface TransactionAttributeSource {default boolean isCandidateClass(Class<?> targetClass) {return true;}TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
}
源码10TransactionDefinition.javapublic interface TransactionAttribute extends TransactionDefinition

由 源码8-10 可知,TransactionAttributeSource有一个getTransactionAttribute方法,该方法的javadoc如下:

Return the transaction attribute for the given method, or null if the method is non-transactional.
返回给定方法的事务属性,如果方法是非事务性的,则返回null。

可见,getTransactionAttribute方法将一个类中的方法解析并封装为TransactionAttribute,而TransactionAttribute本身又是一个TransactionDefinition,因此TransactionAttributeSource的作用就是将一个类中的方法解析并封装为一个事务定义信息TransactionDefinition

借助IDEA可以找到TransactionAttributeSource的几个实现类,其中一个是AnnotationTransactionAttributeSource类。

该类的javadoc如下:

Implementation of the org.springframework.transaction.interceptor.TransactionAttributeSourceinterface for working with transaction metadata in JDK 1.5+ annotation format.
实现了TransactionAttributeSource接口,用于处理 JDK 1.5+ 注释格式的事务元数据。
This class reads Spring’s JDK 1.5+ Transactional annotation.
这个类读取Spring的 JDK 1.5+ 的@Transactional注解。

这说明,AnnotationTransactionAttributeSource类解析事务信息的依据是@Transactional注解,这就是注解声明式事务的标注读取器。

至于是如何读取、解析的,详见 10.4 节。

10.3.5.2 TransactionInterceptor
源码11ProxyTransactionManagementConfiguration.java@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;
}
源码12TransactionInterceptor.javapublic class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable
源码13TransactionAspectSupport.javapublic abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {}

由 源码11-13 可知,TransactionInterceptor事务拦截器,本身是一个MethodInterceptor。

TransactionInterceptor还有一个父类TransactionAspectSupport,其内部集成了一些事务API,如执行事务的核心方法invokeWithinTransaction、创建事务、提交事务、回滚事务等。

至于是如何如何触发这些API的,详见 10.4 节。

10.3.5.3 BeanFactoryTransactionAttributeSourceAdvisor
源码14ProxyTransactionManagementConfiguration.java@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);// 提取@EnableTransactionManagement注解的order属性if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;
}
源码15BeanFactoryTransactionAttributeSourceAdvisor.javapublic class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {@Nullableprivate TransactionAttributeSource transactionAttributeSource;private final TransactionAttributeSourcePointcut pointcut =     new TransactionAttributeSourcePointcut() {@Override@Nullableprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};
}

由 源码14 可知,BeanFactoryTransactionAttributeSourceAdvisor是一个增强器,其内部组合了TransactionInterceptor事务拦截器和TransactionAttributeSource事务配置源。

由 源码15 可知,BeanFactoryTransactionAttributeSourceAdvisor的切入点就是TransactionAttributeSource,其判断一个类是否可以被增强的依据,就是利用TransactionAttributeSource检查一个类和方法是否标注@Transactional注解。

这个逻辑和实际项目开发中的事务控制逻辑是一样的,如果Service类或者方法上标注了@Transactional注解,则事务切面会介入控制。

10.4 声明式事务的控制流程

10.3 节研究了声明式事务的生效原理,本节以 10.1 节的整合项目案例,以Debug的方式研究声明式事务的控制流程。

10.4.1 CglibAopProxy#intercept()

默认情况下,@EnableTransactionManagement注解的proxyTargetClass属性的值为false,因此SpringBoot会使用代理目标类的方式创建代理对象,即CglibAopProxy的内部类DynamicAdvisedInterceptor 的intercept方法。

将断点打在intercept方法上,Debug运行项目可以得到下图:

CglibAopProxy#intercept()
可见,在intercept方法中,会调用getInterceptorsAndDynamicInterceptionAdvice方法获取要执行的增强器。而与声明式事务相关的增强器就是上面 10.3.5.3 节研究的BeanFactoryTransactionAttributeSourceAdvisor,这个增强器中组合的通知Advice,刚好是上面 10.3.5.2 节研究的TransactionInterceptor。

明确了通知Advice,则直接将断点打在TransactionInterceptor的invoke方法上。

10.4.2 TransactionInterceptor#invoke()

源码16TransactionInterceptor.javapublic Object invoke(MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

由 源码16 可知,TransactionInterceptor的invoke方法直接调用了invokeWithinTransaction方法,而该方法定义在TransactionInterceptor的父类TransactionAspectSupport中。

由于invokeWithinTransaction方法篇幅很长,下面拆解来看。

10.4.2.1 获取TransactionAttribute
源码17TransactionAspectSupport.javaprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {TransactionAttributeSource tas = getTransactionAttributeSource();final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// ......
}

由 源码17 可知,invokeWithinTransaction方法的第一步是利用TransactionAttributeSource获取TransactionAttribute,也就是事务定义信息。

源码18AbstractFallbackTransactionAttributeSource.javaprivate static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {@Overridepublic String toString() {return "null";}
};public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// 根据method和targetClass构造一个缓存keyObject cacheKey = getCacheKey(method, targetClass);// 直接从缓存中获取TransactionAttributeTransactionAttribute cached = this.attributeCache.get(cacheKey);if (cached != null) {// 如果获取到的是NULL_TRANSACTION_ATTRIBUTE,则返回空if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;} else {// 如果获取到的不是空,则直接返回return cached;}} else {// 如果缓存中没有,则需要构造出来TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// 无论是否构造成功,都会放置到缓存attributeCache中if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);} else {String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}// logger...this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}

由 源码18 可知,getTransactionAttribute方法内部有一个缓存机制,首先会根据方法和方法所在的类去缓存中寻找TransactionAttribute,找到了直接返回,没找到则进行构造。若构造失败了,也会缓存一个NULL_TRANSACTION_ATTRIBUTE空定义并返回。

要注意的是,将断点打在getTransactionAttribute方法中,发现在解析UserService类的test方法时,从缓存中已经可以直接找到TransactionAttribute:

缓存中的TransactionAttribute不为空
这是因为,在创建事务代理对象时,事务通知Advice就需要与每个正在创建的bean对象进行匹配,而匹配时需要使用TransactionAttributeSource检查方法或方法所在类是否标注了@Transactional注解,以此来判断是否需要对当前正在创建的bean对象织入事务通知。

因此,在真正触发事务拦截器时,UserService类的test方法的TransactionAttribute就已经保存到缓存中了。

由 源码18 可知,如何解析和封装TransactionAttribute,使用的是computeTransactionAttribute方法。

源码19AbstractFallbackTransactionAttributeSource.javaprotected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 非public方法不处理if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// 首先寻找方法上是否标注了@Transaction注解TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 如果方法上没有,则寻找类上是否标注了@Transaction注解txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}// ......return null;
}

由 源码19 可知,解析和封装TransactionAttribute首先会寻找方法上是否标注了@Transaction注解,如果方法上没有,则寻找类上是否标注了@Transaction注解。

总结:当应用启动时,由于@EnableTransactionManagement注解默认生效,该注解会向IOC容器注册InfrastructureAdvisorAutoProxyCreator事务通知增强器,这个增强器会参与bean对象初始化的AOP后置处理逻辑,检查被创建的bean对象是否可以织入事务通知(标注@Transaction注解),检查的动作会同时将TransactionAttribute保存到AbstractFallbackTransactionAttributeSource的缓存中。因此在真正触发事务拦截器的逻辑而需要取出事务定义信息时,可以直接从缓存中取出而不需要重新解析。

10.4.2.2 获取TransactionManager
源码20TransactionAspectSupport.javaprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// 获取TransactionAttribute ......// 获取TransactionManagerfinal TransactionManager tm = determineTransactionManager(txAttr);// ......
}protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {if (txAttr == null || this.beanFactory == null) {return getTransactionManager();}String qualifier = txAttr.getQualifier();if (StringUtils.hasText(qualifier)) {return determineQualifiedTransactionManager(this.beanFactory, qualifier);} else if (StringUtils.hasText(this.transactionManagerBeanName)) {return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);} else {TransactionManager defaultTransactionManager = getTransactionManager();if (defaultTransactionManager == null) {defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);if (defaultTransactionManager == null) {// 最终从BeanFactory中通过getBean方法获取defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}return defaultTransactionManager;}
}

由 源码20 可知,获取到事务定义信息之后,接下来是获取事务管理器,调用的是determineTransactionManager方法,该方法用各种方式获取事务管理器,如果都没有获取到,最终会从BeanFactory中通过getBean方法获取。

在此处打断点可以发现,最终得到一个基于数据源的DataSourceTransactionalManager。

得到一个基于数据源的DataSourceTransactionalManager

10.4.2.3 响应式事务管理器的处理
源码21TransactionAspectSupport.javaprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// 获取TransactionAttribute ......// 获取TransactionManager ......// 响应式事务管理器的处理if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && TransactionAspectSupport.KotlinDelegate.isSuspend(method)) {// throw ......}ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());if (adapter == null) {// throw ......}return new ReactiveTransactionSupport(adapter);});return txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);}// ......
}

由 源码21 可知,下一部分逻辑针对响应式事务。上一步返回的事务管理器的类型是DataSourceTransactionalManager,因此tm instanceof ReactiveTransactionManager的结果是false,不会进入响应式事务的处理逻辑。

10.4.2.4 事务控制的核心
源码22TransactionAspectSupport.javaprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// 获取TransactionAttribute ......// 获取TransactionManager ......// 响应式事务管理器的处理 ......// 事务控制的核心PlatformTransactionManager ptm = asPlatformTransactionManager(tm);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 1.开启事务TransactionAspectSupport.TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// 2.环绕通知,执行Service方法// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();} catch (Throwable ex) {// 3.捕捉到异常,回滚事务completeTransactionAfterThrowing(txInfo, ex);throw ex;} finally {cleanupTransactionInfo(txInfo);}if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {TransactionStatus status = txInfo.getTransactionStatus();if (status != null && txAttr != null) {retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}//4.提交事务commitTransactionAfterReturning(txInfo);return retVal;} // else ......
}

由 源码22 可知,注解声明式事务的核心是一个环绕通知。核心动作有4步:开启事务、执行Service方法、遇到异常时回滚事务、没有异常时提交事务

(1)成功的事务提交

执行完createTransactionIfNecessary方法后,事务成功开启。由于UserService的test方法正常执行,会触发下面的commitTransactionAfterReturning方法提交事务。

源码23TransactionAspectSupport.javaprotected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {if (txInfo != null && txInfo.getTransactionStatus() != null) {// logger ...txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}
}

由 源码23 可知,提交事务的逻辑是获取到事务管理器后执行commit方法提交逻辑。

源码24AbstractPlatformTransactionManager.javapublic final void commit(TransactionStatus status) throws TransactionException {// 如果事务已经完成,则无法提交,抛出异常if (status.isCompleted()) {// throw ......}// 如果事务已经被标记为需要回滚,则回滚事务DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;if (defStatus.isLocalRollbackOnly()) {// logger ... processRollback(defStatus, false);return;}// 如果事务已经标记为全局回滚,则进行回滚if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {// logger ... processRollback(defStatus, true);return;}// 正常情况下提交事务processCommit(defStatus);
}

由 源码24 可知,事务管理器的commit方法并不会直接提交事务,而是会先进行一些异常情况的检查,确保无误后再执行processCommit方法提交事务。

源码25AbstractPlatformTransactionManager.javaprivate void processCommit(DefaultTransactionStatus status) throws TransactionException {try {// ......if (status.hasSavepoint()) {// 存在事务保存点,处理保存点的逻辑unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();} else if (status.isNewTransaction()) {// 新事务,直接提交unexpectedRollback = status.isGlobalRollbackOnly();doCommit(status);}// ......} catch ...} finally {cleanupAfterCompletion(status);}
}

由 源码24 可知,processCommit方法的核心动作是doCommit方法,该方法的实现在落地实现类DataSourceTransactionManager中。

源码26DataSourceTransactionManager.classprotected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();// logger...}try {con.commit();} // catch ...
}

由 源码25 可知,doCommit方法会获取到原生JDBC的Connection,执行其commit方法完成事务提交。

(2)异常的事务回滚

执行完createTransactionIfNecessary方法后,事务成功开启。由于UserService的test方法的执行出现异常catch结构中的completeTransactionAfterThrowing方法回滚事务。

源码26TransactionAspectSupport.javaprotected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {// logger ...// 如果当前异常在回滚范围之内,则会调用事务管理器回滚事务if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());} // catch ......} else {// 如果不再回滚范围内,则依然会提交事务try {txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());} // catch ......}}
}
源码27DefaultTransactionAttribute.javapublic boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);
}

由 源码26 可知,获取到异常后,completeTransactionAfterThrowing方法会根据异常类型决定是否回滚异常,如果当前异常在回滚范围之内,则会调用事务管理器的rollback方法回滚事务。

由 源码27 可知,默认情况下@Transactional注解控制回滚的异常类型包括Error和RuntimeException,对于普通的Exception默认不回滚。

这提示开发者在日常开发中,标注@Transactional时一定要通过设置其rollbackFor属性显式地声明事务回滚的异常类型。

源码28AbstractPlatformTransactionManager.javapublic final void rollback(TransactionStatus status) throws TransactionException {// 如果事务已经完成,则无法继续回滚if (status.isCompleted()) {// throw ...}// 回滚事务DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;processRollback(defStatus, false);
}private void processRollback(DefaultTransactionStatus status, boolean unexpected) {try {// 如果存在保存点,则直接回滚到保存点位置if (status.hasSavepoint()) {// logger ...status.rollbackToHeldSavepoint();} else if (status.isNewTransaction()) {// logger ...// 对于新事物,直接回滚doRollback(status);}// ......} catch ...} finally {cleanupAfterCompletion(status);}
}

由 源码28 可知,rollback方法的核心动作是doRollback方法,该方法的实现在落地实现类DataSourceTransactionManager中。

源码29DataSourceTransactionManager.javaprotected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();// logger ...try {con.rollback();} // catch ...
}

由 源码29 可知,doRollback方法会获取到原生JDBC的Connection,执行其rollback方法完成事务回滚。

10.4.2.5 事务执行的后处理

由 源码25、28 可知,无论是成功提交事务(processCommit方法)还是回滚事务(processRollback方法),最终都会执行一个cleanupAfterCompletion方法。

源码30AbstractPlatformTransactionManager.javaprivate void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();// 组件资源清除if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}// 释放挂起的事务if (status.getSuspendedResources() != null) {// logger ...Object transaction = (status.hasTransaction() ? status.getTransaction() : null);resume(transaction, (AbstractPlatformTransactionManager.SuspendedResourcesHolder) status.getSuspendedResources());}
}

由 源码30 可知,cleanupAfterCompletion方法的前两个if结构与组件资源清除相关,最后一个if结构有一个resume方法,用于释放挂起的事务。

至此,整个事务控制全流程执行完毕。

······

本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/266806.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】五种IO模型与IO多路转接之select

文章目录 一、五种IO模型二、非阻塞IO1.fcntl2.实现函数SetNoBlock3.轮询方式读取标准输入 三、I/O多路转接之select1.初识select2.select函数原型3.socket就绪条件4.select的特点5.select缺点6.select使用案例--只读取数据的server服务器1.err.hpp2.log.hpp3.sock.hpp4.select…

C++笔记(六)--- 静态成员变量/函数(static)

目录 C语言中静态变量 C静态成员变量 C静态成员函数 C语言中静态变量 1.函数内部用static修饰的变量&#xff0c;只能在此函数使用&#xff0c;但所修饰的变量不随函数释放而释放&#xff0c;下次调用时的值仍为上次结束时的值2.static修饰的全局变量只能在其定义的文件使用…

【前端素材】推荐优质后台管理系统 Greeva平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面&#xff0c;通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面&#xff0c;使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…

南方电网的能源棋局上,蔚来换电扮演什么角色?

2 月 26 日&#xff0c;南网储能科技与蔚来能源签署协议&#xff0c;将充换电站、储能站、可调负载等聚合资源连接到虚拟电厂平台&#xff0c;推动换电站作为分布式储能在虚拟电厂项目上的应用。 蔚来换电站是国内首个智慧微电网型分布式换电设施&#xff0c;可透过换电订单预…

【C++】结构体内存对齐详解

规则 1.第一个成员在结构体变量偏移量为0 的地址处&#xff0c;也就是第一个成员必须从头开始。 2.其他成员的偏移量为对齐数**(该成员的大小 与 编译器默认的一个对齐数 中的较小值)**的整数倍。 3.结构体总大小对最大对齐数&#xff08;通过最大成员来确定&#xff09;的整数…

本科毕业设计:计及并网依赖性的分布式能源系统优化研究。(C语言实现)(内包含NSGA II优化算法)(二)

目录 前言 1、sofc函数 2、光伏板函数 3、集热场函数 4、sofc电跟随策略函数 5、二分法找sofc运行点函数 6、目标函数&#xff1a;成本 7、目标函数&#xff1a;二氧化碳排放量 8、目标函数&#xff1a;并网依赖性 前言 本篇文章介绍的是我的毕业设计&#xff0c;我将C…

[游戏开发][虚幻5]新建项目注意事项

鼠标右键点击Client.uproject文件&#xff0c;可以看到三个比较关键的选项&#xff0c; 启动游戏&#xff0c;生成sln解决方案&#xff0c;切换引擎版本 断点调试 C代码重要步骤 如果你想断点调试C代码&#xff0c;则必须使用使用代码编译启动引擎&#xff0c;你需要做几个操作…

java-ssm-jsp广播剧制作订阅系统

java-ssm-jsp广播剧制作订阅系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

YOLOv5白皮书-第Y4周:common.py文件解读

YOLOv5白皮书-第Y4周:common.py文件解读 YOLOv5白皮书-第Y4周:common.py文件解读0.导入需要的包和基本配置1.基本组件1.1 autopad1.2 Conv1.3 Focus1.4 Bottleneck1.5 BottleneckCSP1.6 C31.7 SPP1.8 Concat1.9 Contract、Expand 2.重要类2.1 非极大值抑制&#xff08;NMS&…

makefileGDB使用

一、makefile 1、make && makefile makefile带来的好处就是——自动化编译&#xff0c;一旦写好&#xff0c;只需要一个make命令&#xff0c;整个工程完全自动编译&#xff0c;极大的提高了软件开发的效率 下面我们通过如下示例来进一步体会它们的作用&#xff1a; ①…

【rust】11、所有权

文章目录 一、背景二、Stack 和 Heap2.1 Stack2.2 Heap2.3 性能区别2.4 所有权和堆栈 三、所有权原则3.1 变量作用域3.2 String 类型示例 四、变量绑定背后的数据交互4.1 所有权转移4.1.1 基本类型: 拷贝, 不转移所有权4.1.2 分配在 Heap 的类型: 转移所有权 4.2 Clone(深拷贝)…

[ffmpeg] x264 配置参数解析

背景 创建 x264 编码器后&#xff0c;其有一组默认的编码器配置参数&#xff0c;也可以根据需要修改参数&#xff0c;来满足编码要求。 具体参数 可修改的参数&#xff0c;比较多&#xff0c;这边只列举一些常用的。 获取可以配置的参数 方式1 查看 ffmpeg源码 libx264.c…

apidoc接口文档的自动更新与发布

文章目录 一、概述二、环境准备三、接口文档生成1. 下载源码2. 初始化3.执行 四、文档发布五&#xff0c;配置定时运行六&#xff0c;docker运行七&#xff0c;优化方向 一、概述 最近忙于某开源项目的接口文档整理&#xff0c;采用了apidoc来整理生成接口文档。 apidoc是一个…

Java8 - LocalDateTime时间日期类使用详解

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

HarmonyOS—编译构建概述

编译构建是将应用/服务的源代码、资源、第三方库等&#xff0c;通过编译工具转换为可直接在硬件设备上运行的二进制机器码&#xff0c;然后再将二进制机器码封装为HAP/APP软件包&#xff0c;并为HAP/APP包进行签名的过程。其中&#xff0c;HAP是可以直接运行在模拟器或真机设备…

THINKPHP 跨域报错解决方案

报错&#xff1a;has been blocked by CORS policy: Response to preflight request doesnt pass access control check: No Access-Control-Allow-Origin header is present on the requested resource. 环境&#xff1a;thinkphp6 nginx 今天和VUE配合调用接口的时候发现跨…

utniy urp shinyssrr插件使用

文章目录 前言步骤1首先在URP的配置文件里添加SSR后处理2 修改RenderingPath为延迟渲染3 启用深度纹理4 为物体添加脚本 插件下载 前言 用来实现屏幕空间反射效果 unity 版本为2021.3.8LTS&#xff0c;低版本的untiy URP的参数设置位置z可能会不同 步骤 1首先在URP的配置文件…

【C语言】三子棋

前言&#xff1a; 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏规则是双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利。但因棋盘太小&#xff0c;三子棋在很多时候会出现和…

Java-常用集合

Jva常用集合 一、Java 集合框架体系二、Collection接口和方法1. List接口List 接口主要实现类&#xff1a;ArrayListList 的实现类之二&#xff1a;LinkedListList 的实现类之三&#xff1a;Vector 2. Set接口Set 主要实现类&#xff1a;HashSetSet 实现类之二&#xff1a;Link…

抽象类与抽象方法

文章目录 抽象类抽象类的特点 抽象方法抽象方法的特点 模板设计模式模板设计模式能解决的问题示例 #抽象类与抽象方法 抽象类 用abstract关键字来修饰一个类时&#xff0c;这个类就叫抽象类。 public abstract 类名{... }抽象类的特点 1&#xff09;抽象类不能被实例化。 2&…