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.mzph.cn/news/705376.shtml

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

相关文章

Datawhale-Sora技术原理分享

目录 Sora能力边界探索 Sora模型训练流程 Sora关键技术拆解 物理引擎的数据进行训练 个人思考与总结 参考 https://datawhaler.feishu.cn/file/KntHbV3QGoEPruxEql2c9lrsnOb

袁庭新ES系列12节 | Elasticsearch高级查询操作

前言 上篇文章讲了关于Elasticsearch的基本查询操作。接下来袁老师为大家带来Elasticsearch高级查询部分相关的内容。Elasticsearch是基于JSON提供完整的查询DSL&#xff08;Domain Specific Language&#xff1a;领域特定语言&#xff09;来定义查询。因此&#xff0c;我们有…

消息中间件篇之Kafka-消息不丢失

一、 正常工作流程 生产者发送消息到kafka集群&#xff0c;然后由集群发送到消费者。 但是可能中途会出现消息的丢失。下面是解决方案。 二、 生产者发送消息到Brocker丢失 1. 设置异步发送 //同步发送RecordMetadata recordMetadata kafkaProducer.send(record).get();//异…

【Java程序设计】【C00296】基于Springboot的4S车辆管理系统(有论文)

基于Springboot的4S车辆管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的4S店车辆管理系统 本系统分为销售员功能模块、管理员功能模块以及维修员功能模块。 管理员功能模块&#xff1a;管理员登录进入4S…

少儿编程热潮背后的冷思考、是不是“智商税”?

在科技飞速发展的今天&#xff0c;编程已成为一项基础技能&#xff0c;如同数学和语言一样&#xff0c;被认为是未来社会的重要通行证。随之而来的是少儿编程教育的火爆&#xff0c;各种编程班、在线课程如雨后春笋般涌现&#xff0c;吸引了无数家长的目光。然而&#xff0c;这…

测试环境搭建整套大数据系统(七:集群搭建kafka(2.13)+flink(1.14)+dinky+hudi)

一&#xff1a;搭建kafka。 1. 三台机器执行以下命令。 cd /opt wget wget https://dlcdn.apache.org/kafka/3.6.1/kafka_2.13-3.6.1.tgz tar zxvf kafka_2.13-3.6.1.tgz cd kafka_2.13-3.6.1/config vim server.properties修改以下俩内容 1.三台机器分别给予各自的broker_id…

MapGIS农业信息化解决方案(2)

农业资源采集与调查 农业各项生产活动与农业资源息息相关,对农业资源进行调查,摸清农业家底, 为构筑农业“一张图”核心数据库奠定数据基础。MapGIS 农业资源采集与调查系统集成遥感、手持终端等调查技术,为农业资源采集提供实用、简捷的采集调查和信息录入工具,实现农田…

PCB设计十大黄金准则

PCB设计十大黄金准则 控制走线长度控制走线长度&#xff0c;顾名思义&#xff0c;即短线规则&#xff0c;在进行PCB设计时应该控制布线长度尽量短&#xff0c;以免因走线过长引入不必要的干扰&#xff0c;特别是一些重要信号线&#xff0c;如时钟信号走线&#xff0c;务必将其…

linux查看socket信息

netstat netstat 是一个用于显示网络相关信息的命令行工具。它可以显示当前系统的网络连接状态、路由表、接口统计信息等。 下面是一些常见的 netstat 命令选项和用法&#xff1a; 显示所有活动的网络连接&#xff1a; netstat -a 显示所有正在监听的端口&#xff1a; ne…

深度学习 精选笔记(4)线性神经网络-交叉熵回归与Softmax 回归

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

现代化数据架构升级:毫末智行自动驾驶如何应对年增20PB的数据规模挑战?

毫末智行是一家致力于自动驾驶的人工智能技术公司&#xff0c;其前身是长城汽车智能驾驶前瞻分部&#xff0c;以零事故、零拥堵、自由出行和高效物流为目标&#xff0c;助力合作伙伴重塑和全面升级整个社会的出行及物流方式。 在自动驾驶领域中&#xff0c;是什么原因让毫末智行…

【设计模式】5种创建型模式详解

创建型模式提供创建对象的机制,能够提升已有代码的灵活性和复用性。 常用的有:单例模式、工厂模式(工厂方法和抽象工厂)、建造者模式。不常用的有:原型模式。一、单例模式 1.1 单例模式介绍 1 ) 定义 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模…

Jupyterlab 和 JupyternoteBook 修改默认路径

Jupyterlab 和 JupyternoteBook 修改默认路径 在使用 JupyterLab 或 Jupyter Notebook 进行数据分析、机器学习项目时&#xff0c;经常会遇到需要修改默认工作目录的需求。默认情况下&#xff0c;JupyterLab 和 Jupyter Notebook 会在启动时打开你的用户目录&#xff08;例如&…

Linux 不同架构、不同系统的问题

文章目录 一、麒麟V10&#xff08;kylin&#xff09;操作系统中&#xff0c;sudo执行程序后&#xff0c;其环境变量依然为用户家目录。&#xff08;1&#xff09;背景&#xff08;2&#xff09;原因&#xff08;3&#xff09;解决办法 二、统信&#xff08;UOS&#xff09;操作…

GDB之(1)入门指令参数介绍

GDB之(1)基础入门指令参数介绍 Author&#xff1a;Once Day Date: 2022年7月29日/2024年2月26日 漫漫长路&#xff0c;才刚刚开始… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSDN博客 推荐参考文档&#xff1a; GDB: The GNU Project Debugger (sourceware.o…

机器学习 | 基本概念梳理——数据集评估,任务,训练和测试,期望结果

文章目录 1 整体概念梳理1.1 数据集与数据术语——原材料1.2 任务术语——目标1.3 训练和测试术语——怎么做1.4 结果——预期期望 整体框架 机器学习的基本概念全梳理 我们通过一个生动形象的例子来介绍这些概念 我们假设有一个任务是根据地理天气等特征位置预测经纬度 1 整…

2023 re:Invent 用 Amazon Q 打造你的知识库

前言 随着 ChatGPT 的问世&#xff0c;我们迎来了许多创新和变革的机会。一年一度的亚马逊云科技大会 re:Invent 也带来了许多前言的技术&#xff0c;其中 Amazon CEO Adam Selipsky 在 2023 re:Invent 大会中介绍 Amazon Q 让我印象深刻&#xff0c;这预示着生成式 AI 的又一…

VUE从0到1创建项目及基本路由、页面配置

一、创建项目:(前提已经安装好vue和npm) 目录:E:\personal\project_pro\ windows下,win+R 输入cmd进入命令行: cd E:\personal\project_pro E:# 创建名为test的项目 vue create test# 用上下键选择vue2或vue3,回车确认创建本次选择VUE3 创建好项目后,使用…

Nginx之rewrite重写功能

一、rewrite概述 1、rewrite功能 访问重写 rewrite 是 Nginx HTTP 请求处理过程中的一个重要功能&#xff0c;它是以模块的形式存在于代码中的&#xff0c;其功能是对用户请求的 URI 进行 PCRE 正则重写&#xff0c;然后返回 30 重定向跳转或按条件执行相关配置。 Nginx服务…

idea 创建打包 android App

1、使用 idea 创建 android 工程 2、 配置构建 sdk 3、配置 gradle a、进入 gradle 官网&#xff0c;选择 install &#xff08;默认是最新版本&#xff09; b、选择包管理安装&#xff0c;手动安装选择下面一个即可 c、安装 sdk 并通过 sdk 安装 gradle 安装 sdk&#xff1a…