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://xiahunao.cn/news/2812872.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot+vue的抗疫物资管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

C 嵌入式系统设计模式 12:去抖动模式

本书的原著为&#xff1a;《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》&#xff0c;讲解的是嵌入式系统设计模式&#xff0c;是一本不可多得的好书。 本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之五&…

(undone) 如何计算 Hessian Matrix 海森矩阵 海塞矩阵

参考视频1&#xff1a;https://www.bilibili.com/video/BV1H64y1T7zQ/?spm_id_from333.337.search-card.all.click 参考视频2&#xff08;正定矩阵&#xff09;&#xff1a;https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_…

【Datawhale组队学习:Sora原理与技术实战】Sora技术原理

Sora能力边界探索 最大支持60秒高清视频生成&#xff0c;以及基于已有短视频的前后扩展&#xff0c;同时保持人物/场景的高度一致性如奶茶般丝滑过渡的视频融合能力同一场景的多角度/镜头的生成能力具有动态摄像机运动的视频。随着摄像机的移动和旋转&#xff0c;人和其 他场景…

SpaceX 首次通过星链 从太空向社交平台 X 上发帖

2 月 26 日&#xff0c;伊隆马斯克旗下太空探索技术公司 SpaceX 宣布&#xff0c;该公司已成功地通过「星链」卫星网络&#xff0c;从太空向社交平台 X 上发布了第一个帖子。 这一次的发布&#xff0c;是通过手机卫星服务——「卫星直连蜂窝网络」&#xff08;Direct to Cell&…

vscode不能远程连接ubuntu18.04.6

目录 问题解决Portable Mode 安装vscode 补充说明学习资料 问题 vscode远程ssh连接ubuntu18.04.6时&#xff0c;出现如下提示框&#xff0c;单击Learn More后&#xff0c;定位到问题。Can I run VS Code Server on older Linux distributions? 原始是&#xff1a;需要glibc …

递归和迭代【Py/Java/C++三种语言详解】LeetCode每日一题240218【树DFS】LeetCode 589、 N 叉树的前序遍历

有LeetCode算法/华为OD考试扣扣交流群可加 948025485 可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1336了解算法冲刺训练 文章目录 题目描述解题思路代码方法一&#xff1a;递归法PythonJavaC时空复杂度 方法二&#xff1a;迭代法PythonJavaC时空复杂度 …

北邮毕业论文Latex模板使用教程(Windows)

1latex模板下载 下载地址&#xff1a; https://github.com/rioxwang/BUPTGraduateThesis2安装编译环境 TEX Live 2014 或者CTEX 2.9.2.164&#xff0c;以及更高的版本. 下载其中一个即可 &#xff08;1&#xff09;TEX Live下载地址&#xff1a; https://tug.org/texlive/acq…

JAVA学习笔记12

1.键盘输入语句 1.1 介绍 ​ *在编程中&#xff0c;需要接收用户输入的数据&#xff0c;就可以使用键盘输入语句来获取。 1.2 步骤 ​ 1.导入该类的所在包&#xff0c;java.util.* ​ 2.创建该类对象&#xff08;声明变量&#xff09; ​ 3.调用里面的功能 import java.…

Aigtek前置微小信号放大器在传感器检测中的应用有哪些

传感器是将物理量转换为电信号的装置&#xff0c;其精度和灵敏度直接影响到检测系统的性能。而传感器的输出信号通常都非常微弱&#xff0c;需要进行放大处理才能得到可靠的测量结果。前置微小信号放大器&#xff0c;作为一种重要的传感器检测元件&#xff0c;在传感器检测中发…

游戏服务器租用价格大比拼,腾讯云阿里云京东云疯狂比价!

游戏服务器租用多少钱一年&#xff1f;1个月游戏服务器费用多少&#xff1f;阿里云游戏服务器26元1个月、腾讯云游戏服务器32元&#xff0c;华为云26元&#xff0c;游戏服务器配置从4核16G、4核32G、8核32G、16核64G等配置可选&#xff0c;游戏专业服务器公网带宽10M、12M、15M…

CentOS7 Hive2.3.8安装

CentOS7 Hive2.3.8 安装 建议从头用我的博客&#xff0c;如果用外教的文件到 一、9)步骤了&#xff0c;就用他的弄完&#xff0c;数据库不一样&#xff0c;在9步骤前还能继续看我的 一、 安装MySQL 0.0&#xff09;查询mariadb,有就去0.1&#xff09;&#xff0c;没有就不管…

x-cmd pkg | g - 功能和交互更为丰富的 `ls` 替代方案

目录 简介首次用户功能特点竞品和相关作品进一步阅读 简介 g 是一项用 Go 开发的、功能和交互更为丰富的 ls 替代方案。它拥有 100 多个功能选项&#xff0c;主要是通过各式图标、各种布局选项和 git status 集成来增强视觉效果&#xff0c;并且支持多种输出格式&#xff0c;如…

Administrative-divisions-of-China:中华人民共和国行政区划数据

Administrative-divisions-of-China是中华人民共和国行政区划&#xff1a;省级&#xff08;省份&#xff09;、 地级&#xff08;城市&#xff09;、 县级&#xff08;区县&#xff09;、 乡级&#xff08;乡镇街道&#xff09;、 村级&#xff08;村委会居委会&#xff09; &a…

c++史上最全算法详解,0基础可秒懂!(爆肝4万字)

本文极长&#xff0c;建议点赞收藏后看&#xff01; 质量分95&#xff01;&#xff01; 文章目录 -1.C 标准0.语法基础1. C头文件2. C命名空间3. 主函数4. 变量类型5. ASCII码6. 注释 1.顺序结构一、代码示例二、例题1&#xff1a;求圆的面积三、例题2&#xff1a;求解一元二次…

常见集合框架底层原理

常见集合框架底层原理 常见的集合有哪些 Java集合类主要由两个接口Collection和Map派生出来的&#xff0c;Collection有三个子接口: List、 Set、Queue List代表了有序可重复集合&#xff0c;可直接根据元素的索引来访问Set代表了无序集合&#xff0c;只能根据元素本身来访问…

【Leetcode每日一题】二分查找 - 有效的完全平方数(难度⭐)(19)

1. 题目解析 Leetcode链接&#xff1a;367. 有效的完全平方数 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于判断给定的整数是否可以开方成两个整数相乘&#xff0c;可以就返回false&#xff0c;反之返回true。 2. 算法…

【c语言】基础数据类型

文章目录 1、什么数据类型2、常量3、变量4、整型数据5、浮点型数据6、字符型数据7、字符串数据 1、什么数据类型 ​ 在生活中&#xff0c;裁缝做衣服需要用到不同的化纤、棉花、丝绸等布料&#xff0c;炒不同的菜需要油、盐等不同的配方&#xff0c;而程序员在编写程序时也需要…

4核8G服务器多少钱?腾讯云和阿里云哪家便宜?

4核8G云服务器多少钱一年&#xff1f;阿里云ECS服务器u1价格955.58元一年&#xff0c;腾讯云轻量4核8G12M带宽价格是646元15个月&#xff0c;阿腾云atengyun.com整理4核8G云服务器价格表&#xff0c;包括一年费用和1个月收费明细&#xff1a; 云服务器4核8G配置收费价格 阿里…

Vue.js+SpringBoot开发快递管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快递区域模块2.4 快递货架模块2.5 快递档案模块 三、界面展示3.1 登录注册3.2 快递类型3.3 快递区域3.4 快递货架3.5 快递档案3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 …