Spring事务注解@Transactional的流程和源码分析

Spring事务简介

Spring事务有两种方式:

  1. 编程式事务:编程式事务通常使用编程式事务管理API实现,比如Spring提供的PlatformTransactionManager接口,使用它操控事务。
  2. 声明式事务:注解式事务使用AOP(面向切面编程)来实现,在方法调用前后添加事务处理逻辑。除了XML配置外,Spring还支持使用注解来声明事务,目前使用注解是更方便和主流的方式。

Spring注解式事务原理

  1. Spring在实例化bean时会将带有@Transactional注解的类生成一个代理类对象,注入使用这个bean就是使用代理类对象。
  2. 这个代理类对象的方法拦截器类似Spring AOP的@Around,把业务方法包裹在事务的开启、事务的提交和事务的回滚,从而让代理类拦截器自动处理事务。
  3. 事务同步管理器将事务状态和信息存储在线程的ThreadLocal里,通过取ThreadLocal的事务信息实现方法嵌套调用中的事务信息传递。

以下这张流程图来自Spring官网:

在这里插入图片描述


Spring事务总览

Spring事务处理有以下几个重要组件:

  • 代理对象:假设SimpleServiceA是原始类,那么在Spring初始化bean时会进行增强生成代理类SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492的实例,代理对象对目标方法进行功能拦截器,自动完成事务的开启、提交和回滚。

  • 事务拦截器 TransactionInterceptor:事务拦截器将事务管理逻辑与业务代码进行解耦,作用类似于Spring AOP的一个拦截器,用于在方法调用前后实现事务管理。

  • 事务管理器 PlatformTransactionManager: Spring事务管理器负责管理事务的生命周期,包括事务的开始、提交或回滚等操作,其中最常用的事务管理器实现类是DataSourceTransactionManager,DataSourceTransactionManager除了上述的功能,还提供了方法使用数据源去获取和返还数据连接。

  • 事务同步管理器 TransactionSynchronizationManager:Spring 框架中用于管理事务同步的工具类,将事务信息和状态和线程绑定。

Spring事务组件关系图:

在这里插入图片描述

事务有关的信息和状态类

Spring事务在处理流程中有一系列的信息和状态类,让人眼花缭乱,下面来列举这些类,理清它们的关系。

  • TransactionInfo:事务信息,包含几乎所有事务信息和状态,在源码里简称为txInfo,内含TransactionAttribute(事务定义)、TransactionStatus(事务状态)和事务管理器。
  • TransactionAttribute:事务定义,里面存放着事务属性。
  • TransactionStatus:事务状态信息,在源码里简称为status,实现类为DefaultTransactionStatus,内含事务对象DataSourceTransactionObjectsavepoint保存点和挂起的数据库连接资源suspendedResources
  • DataSourceTransactionObject:事务对象,在源码里简称为txObjecttransaction,内含数据库连接ConnectionHolder
  • ConnectionHolder:数据库连接持有者,持有着数据库连接。

方法嵌套调用的情况下,每个事务拦截器进来后都会创建方法自身的TransactionInfo、TransactionStatus、DataSourceTransactionObject。

事务有关的信息和状态类关系图:

在这里插入图片描述

事务定义类

在事务处理中事务的定义类TransactionDefinition有很多衍生类和接口,它们的属性从事务注解@Transactional解析而来,在原码里名称为txAttr,关系如下:

在这里插入图片描述

其中DelegatingTransactionAttribute的类图:

在这里插入图片描述

图上那一系列类和接口,虽然比较绕,但都是和事务的定义有关。

事务对象

事务对象跟踪和管理与事务相关的状态和资源,包含数据库连接connectionHolder,实现类为DataSourceTransactionObject,在源码里简称为txObjecttransaction,它的类图如下:

在这里插入图片描述

事务管理器

事务管理器 PlatformTransactionManager 是Spring负责管理事务的生命周期的事务管理器,包括事务的开始、提交或回滚等操作,通过将事务信息和数据库连接绑定至线程实现多个嵌套方法间的传播。
事务管理器内部存放着数据源DataSource,能通过数据源获取和返还数据库连接,对事务的处理就是通过数据源的连接连接进行的。
事务管理器实现类有 DataSourceTransactionManager、JtaTransactionManager、WebSphereUowTransactionManager等,此处分析的是最常用的实现类DataSourceTransactionManager

事务管理器类图

在这里插入图片描述

@Transactional注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};}

@Target({ElementType.METHOD ElementType.TYPE}): 指定此注解可以应用的位置,可以是方法或类型(类和接口)。

@Retention(RetentionPolicy.RUNTIME): 表示此注解应在运行时保留,允许在运行时反射检查它。

注解属性:

  • valuetransactionManager的别名,和transactionManager的作用一样。
  • transactionManager:事务管理器,Spring事务支持使用多个事务管理器,可以通过该属性使用指定事务管理器。
  • propagation: 事务传播方式,它的默认值是 Propagation.REQUIRED。
  • readOnly: 指定事务是否为只读,默认值是 false。
  • isolation: 指定事务的隔离级别,默认值是 Isolation.DEFAULT,即数据库设定的隔离级别。
  • timeout:事务超时时间,默认为数据库设定的时间。
  • rollbackFor:设定要回滚事务的异常类,当捕获到这些异常时回滚,否则不回滚。
  • rollbackForClassName:设定要回滚事务的异常类名称,当捕获到这些异常时回滚,否则不回滚。
  • noRollbackFor:设定不回滚事务的异常类,当捕获到这些异常时不回滚。
  • noRollbackForClassName:设定不回滚事务的异常类名称,当捕获到这些异常时不回滚。
事务传播方式 Propagation
传播方式备注
REQUIRED当前有事务时沿用现有事务,不存在的时候新建一个事务,是默认传播方式。
SUPPORTS当前有事务时沿用现有事务,没有的时候不使用事务。
MANDATORY当前有事务时沿用现有事务,不存在时抛出异常。
REQUIRES_NEW创建新事务,若存在原有事务则挂起原事务。
NOT_SUPPORTED不使用事务,若存在原有事务则挂起原事务。
NEVER不使用事务,若存在原有事务则抛出异常。
NESTED开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务, 嵌套事务开始执行时, 它将取得一个 savepoint。 如果这个嵌套事务失败, 我们将回滚到此 savepoint。嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
事务隔离级别 Isolation
隔离级别中文名称备注
READ_UNCOMMITTED读未提交最低的隔离级别,一个事务可以读取另一个事务未提交的数据。可能会导致脏读、幻读或不可重复读。
READ_COMMITTED读已提交一个事务只能读取已经提交的数据,但是在同一事务中,多次读取同一数据可能会得到不同的结果。
REPEATABLE_READ可重复读一个事务在同一数据上进行多次读取时,可以得到相同的结果,但是在同一事务中,其他事务插入的数据对该事务不可见。
SERIALIZABLE串行化最高的隔离级别,所有事务串行执行,保证数据的一致性和完整性。

代理类

Spring实例化bean时生成事务增强的代理类,如下图的例子,SimpleController里的@Autowired SimpleService1 simpleService注入bean不是原始的类实例,而是一个Spring实例化bean时使用Cglib生成代理类,本例里是
SimpleServiceA$$EnhancerBySpringCGLIB$$1577407e。这个代理类继承自SimpleServiceA,它的作用是向类增加功能拦截器进行链式处理,对事务注解来说就是添加一个TransactionInterceptor增加事务的处理。
在这里插入图片描述

这个代理类编译或打包后是搜索不到的,在Spring启动后动态生成,在Spring应用启动运行时可通过arthas或jad等工具反编译得到。

从这个类的代码可以看到代理类继承原类SimpleServiceA,代理类的updateA()对原始updateA()方法进行拦截。

代理类代码:

public class SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492
extends SimpleServiceA
implements SpringProxy,
Advised,
Factory {private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private MethodInterceptor CGLIB$CALLBACK_1;private NoOp CGLIB$CALLBACK_2;private Dispatcher CGLIB$CALLBACK_3;private Dispatcher CGLIB$CALLBACK_4;private MethodInterceptor CGLIB$CALLBACK_5;private MethodInterceptor CGLIB$CALLBACK_6;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$updateA$0$Method;private static final MethodProxy CGLIB$updateA$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static {SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492.CGLIB$STATICHOOK144();SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492.CGLIB$STATICHOOK143();}static void CGLIB$STATICHOOK144() {try {return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}static void CGLIB$STATICHOOK143() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class<?> clazz = Class.forName("cn.massivestars.service.impl.SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492");Class<?> clazz2 = Class.forName("java.lang.Object");Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());CGLIB$equals$1$Method = methodArray[0];CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = methodArray[1];CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = methodArray[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = methodArray[3];CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");clazz2 = Class.forName("cn.massivestars.service.impl.SimpleServiceA");//原始方法CGLIB$updateA$0$Method = ReflectUtils.findMethods(new String[]{"updateA", "()V"}, clazz2.getDeclaredMethods())[0];//代理方法CGLIB$updateA$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "updateA", "CGLIB$updateA$0");}}//代理方法和原始方法不一样,作了功能增强public final void updateA() throws Exception {try {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {SimpleServiceA$$EnhancerBySpringCGLIB$$22b9f492.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {//使用代理方法对原始方法updateA()进行了功能增强Object object = methodInterceptor.intercept(this, CGLIB$updateA$0$Method, CGLIB$emptyArgs, CGLIB$updateA$0$Proxy);return;}//没有功能增强时使用原始方法super.updateA();return;}catch (Error | Exception | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}//省略代码……
}

代理类的生成

Spring在初始化bean实例的时候过检查一系列条件来确定是否需要对 Bean 进行代理,比如对有@Transactional注解的bean生成额外的增强的代理对象。由于篇幅问题下面的代码分析忽略Spring对一般bean初始化的过程,从生成bean的代理对象开始,即AbstractAutoProxyCreator#postProcessAfterInitialization()方法开始分析,可以跟着这个调用栈跟踪代理对象的生成。

在这里插入图片描述

spring bean功能自动代理部分:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {//开始包裹成代理类return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (beanName != null && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}//1、查找需要生成的功能增强Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//2、创建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}//3、创建类代理工厂ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}//4、构建增强Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);for (Advisor advisor : advisors) {proxyFactory.addAdvisor(advisor);}proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}//5、使用代理工厂创建代理return proxyFactory.getProxy(getProxyClassLoader());}}

代理工厂部分:

public class ProxyFactory extends ProxyCreatorSupport {public Object getProxy(ClassLoader classLoader) {//6、创建aop代理//7、返回代理对象return createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}//6.1 使用aop工厂创建aop代理return getAopProxyFactory().createAopProxy(this);}}public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {//6.2 创建aop代理public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}//创建cglib aop代理对象return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}}

cglib代理对象部分:

class ObjenesisCglibAopProxy extends CglibAopProxy {public ObjenesisCglibAopProxy(AdvisedSupport config) {super(config);}//7.1 返回增强的代理对象public Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");//省略代码……//创建增强器Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}//配置增强器enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);//创建代理类并实例化return createProxyClassAndInstance(enhancer, callbacks);}//省略代码...}protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {Class<?> proxyClass = enhancer.createClass();Object proxyInstance = null;if (objenesis.isWorthTrying()) {try {//创建对象,objenesis创建对象时可以不调用类的构造函数proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());}catch (Throwable ex) {logger.debug("Unable to instantiate proxy using Objenesis, " +"falling back to regular proxy construction", ex);}}//省略代码……((Factory) proxyInstance).setCallbacks(callbacks);return proxyInstance;}}

由于篇幅问题,对spring aop生成代理对象的内容这里只作简略介绍。

代理类责任链模式处理

Spring的bean可以有多个功能增强,@Transactional事务处理是其中一种,还有@Cacheable缓存、@Async异步执行和@Retryable重试等;处理拥有多个功能增强的代理类,Spring使用了一种叫责任链的设计模式。

下面是有@Transactional@Cacheable@Retryable三个功能增强的责任链:

在这里插入图片描述

下图为责任链链式处理的调用调用栈:

在这里插入图片描述


从代理对象到业务方法的过程

从代理对象开始是如何执行到事务拦截器的呢?从上面的代理类代码可以看到调用代理类的方法时,在有功能增强时实际调用的是MethodInterceptorintercept(..)方法,MethodInterceptor实现类是DynamicAdvisedInterceptor,在intercept(..)里,获取功能增强责任链,如果责任链不为空,创建CglibMethodInvocation对象进行处理责任链。

从代理类到业务方法的时序图

在这里插入图片描述

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {private final AdvisedSupport advised;public DynamicAdvisedInterceptor(AdvisedSupport advised) {this.advised = advised;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Class<?> targetClass = null;Object target = null;try {if (this.advised.exposeProxy) {oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}target = getTarget();if (target != null) {targetClass = target.getClass();}//1、获取功能增强责任链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {//责任链为空Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = methodProxy.invoke(target, argsToUse);}else {//2、责任链不为空retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null) {releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}//省略代码……}
}private static class CglibMethodInvocation extends ReflectiveMethodInvocation {//3、处理责任链public Object proceed() throws Throwable {if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {//如果责任链已处理完成,调用业务方法return invokeJoinpoint();}//处理责任链的下一个功能增强Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {return proceed();}}else {//调用具体的拦截器return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}
}

下面是一个有@Cacheable、@Retryable和@Transactional注解的调用栈,很清晰的展示代理对象功能增强的调用过程:

cn.massivestars.service.SimpleServiceA.updateA(SimpleServiceA.java:33)
cn.massivestars.service.SimpleServiceA$$FastClassBySpringCGLIB$$6479dd5.invoke(<generated>)
org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.cache.interceptor.CacheInterceptor$1.invoke(CacheInterceptor.java:52)
org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:345)
org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:414)
org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327)
org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:74)
org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:276)
org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:157)
org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:101)
org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:118)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
cn.massivestars.service.SimpleServiceA$$EnhancerBySpringCGLIB$$3b7e878d.updateA(<generated>)
cn.massivestars.controller.SimpleController.update(SimpleController.java:28)

事务处理的核心方法

事务的处理和我们自己手写的类似,使用try catch代码块包裹业务方法,在业务方法前开启事务,捕获异常时回滚事务,正常运行时最后提交事务。

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {//事务关键代码//这个方法继承自TransactionAspectSupport,代码在TransactionAspectSupportprotected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);          //取事务管理器final String joinpointIdentification = methodIdentification(method, targetClass);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {			//如果必要,创建事务TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null;try {//责任链的下一个拦截处理器,在最后一个拦截器处理完后就是业务代码retVal = invocation.proceedWithInvocation();   }catch (Throwable ex) {//出现异常回滚事务completeTransactionAfterThrowing(txInfo, ex);  throw ex;}finally {//还原为原来的事务信息txInfocleanupTransactionInfo(txInfo);}//提交事务commitTransactionAfterReturning(txInfo);return retVal;}//省略代码……}}

有关的类和组件都已经介绍完毕,下面看看事务管理器处理事务的开始、提交和回滚是怎样实现。


开始事务

开始事务流程如下:

  1. 创建一个事务对象DataSourceTransactionObject,其名称为transaction,将事务对象关联线程绑定的数据库连接(ConnectionHolder),如果线程没有绑定的连接那么原来就是没有存在事务,该事务对象状态为新。
  2. 根据事务对象transaction判断是不是已存在事务,根据原来是否已经存在事务作2种不同的处理方式。

存在事务,根据事务的传播方式作不同处理:

  • PROPAGATION_NEVER:抛出异常。
  • PROPAGATION_NOT_SUPPORTED:不作事务处理。
  • PROPAGATION_REQUIRES_NEW:使用新的数据库连接,开启新事务B,将B的事务信息和状态绑定到线程,同时挂起原来的事务A,在新事务B完成后线程绑定的事务信息恢复为原事务A。
  • PROPAGATION_NESTED:开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务, 嵌套事务开始执行时,它将取得一个 savepoint。如果这个嵌套事务失败, 将回滚到此 savepoint。嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

不存在事务,根据事务的传播方式作不同处理::

  • PROPAGATION_MANDATORY,抛出异常,事务拦截器捕抓异常后回滚事务,结束事务处理。
  • PROPAGATION_REQUIRED | PROPAGATION_REQUIRES_NEW | PROPAGATION_NESTED:从数据源获取连接,开启事务,将事务信息和状态绑定到线程。
  • 其它事务传播方式:创建空事务对象,不真正去处理事务,只是将事务信息和状态绑定到线程。
  1. 事务开启完成后配制事务信息TransactionInfo txInfo,将当前方法事务信息绑定到线程并保留原事务信息以便方法嵌套调用的时候可以恢复。

开始事务源码(事务拦截器部分):

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {//如果事务有名称,将txAttr替换成有事务名称的DelegatingTransactionAttribute实例(相比原来的类多了事务名称)if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {//如果事务管理器不为null,开启事务status = tm.getTransaction(txAttr);}}//配制事务信息,事务信息txInfo绑定至当前线程return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);if (txAttr != null) {txInfo.newTransactionStatus(status);}// 即使在这里没有创建新的事务,始终将 TransactionInfo 绑定到线程。// 这确保了即使没有由此切面创建事务,TransactionInfo 堆栈也将被正确管理。txInfo.bindToThread();return txInfo;}protected final class TransactionInfo {private void bindToThread() {//绑定当前方法事务信息,保留原来事务信息以便当前方法事务结束后恢复this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);}}//省略代码...
}

开始事务源码(事务管理器部分):

public class DataSourceTransactionManager extends AbstractPlatformTransactionManagerimplements ResourceTransactionManager, InitializingBean {//数据源private DataSource dataSource;//此方法继承自AbstractPlatformTransactionManager,代码在AbstractPlatformTransactionManagerpublic final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {//创建一个新的事务对象transaction//如果线程已存在事务,事务对象transaction使用原有的连接Object transaction = doGetTransaction();if (definition == null) {definition = new DefaultTransactionDefinition();}if (isExistingTransaction(transaction)) {//存在事务,根据事务的传播方式作不同处理return handleExistingTransaction(definition, transaction, debugEnabled);}//省略代码...//以下代码为不存在事务时的处理if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {//如果事务传播方式为MANDATORY则抛出异常throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {//这里挂起一个空事务是为了写法的一致性,没有实际作用SuspendedResourcesHolder suspendedResources = suspend(null);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);//真正开始事务doBegin(transaction, definition);//同步事务信息prepareSynchronization(status, definition);return status;}catch (RuntimeException ex) {resume(null, suspendedResources);throw ex;}catch (Error err) {resume(null, suspendedResources);throw err;}}else {//创建空事务对象,不真正去处理事务,只是将事务信息和状态绑定到线程boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);txObject.setConnectionHolder(conHolder, false);  //将事务对象内的数据库连接设置为线程绑定的那个return txObject;}protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (txObject.getConnectionHolder() == null ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {//原来不存在事务,从数据源获取连接	Connection newCon = this.dataSource.getConnection();txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();//设置事务隔离级别Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);//如果设置了事务自动提交,改为手动提交if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);con.setAutoCommit(false);}txObject.getConnectionHolder().setTransactionActive(true);//设置事务超时时间int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}//将数据库连接绑定到线程if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {//如果出现异常,归还还数据库连接,if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, this.dataSource);txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}}	
}

挂起事务

嵌套事务是指在一个事务内部开启了另一个事务。当一个事务内部开启了另一个事务时,外部事务会暂时挂起,直到内部事务执行完成。在源码中多次出现suspend()方法挂起原事务,挂起事务的目的在于:

支持嵌套事务:在某些事务传播行为下,比如PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED,Spring需要支持嵌套事务。当这些传播行为发生时,当前正在执行的事务可能需要被挂起,以便开始一个新的事务。挂起原事务可以暂时中断当前事务的执行,让新的事务能够开始执行。

保存原事务的状态:挂起原事务会保存原事务的状态信息,以便在新事务执行完毕后能够正确地恢复原事务的状态。这对于事务管理的一致性和可靠性非常重要。

处理事务异常:在执行新事务的过程中,可能会发生异常。挂起原事务可以确保异常的发生不会影响到原事务的状态,保证事务的正确回滚和异常处理。

挂起事务的有关源码:

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {//该方法继承自父类AbstractPlatformTransactionManager,代码在父类AbstractPlatformTransactionManagerprotected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {if (TransactionSynchronizationManager.isSynchronizationActive()) {List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;   //挂起的数据库连接持有者           if (transaction != null) {suspendedResources = doSuspend(transaction); //挂起事务}//将线程绑定的原事务信息清除,返回原事务信息作为挂起事务信息String name = TransactionSynchronizationManager.getCurrentTransactionName();TransactionSynchronizationManager.setCurrentTransactionName(null);boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();TransactionSynchronizationManager.setActualTransactionActive(false);return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);}catch (RuntimeException ex) {//还原事务管理器注册的TransactionSynchronization对象doResumeSynchronization(suspendedSynchronizations);throw ex;}catch (Error err) {//还原事务管理器注册的TransactionSynchronization对象doResumeSynchronization(suspendedSynchronizations);throw err;}}else if (transaction != null) {// Transaction active but no synchronization active.Object suspendedResources = doSuspend(transaction);return new SuspendedResourcesHolder(suspendedResources);}else {// Neither transaction nor synchronization active.return null;}}//将事务对象的数据库连接设置为空,将线程绑定的数据库连接解绑protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;txObject.setConnectionHolder(null);ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.unbindResource(this.dataSource);return conHolder;}}

恢复事务

上面介绍了事务的挂起,既然有挂起就有恢复,挂起事务是和恢复事务配套使用的;在事务的提交processCommit()和回滚processRollback()的finally代码块都会执行cleanupAfterCompletion()方法,这个方法里,如果存在挂起的事务信息时,会恢复挂起的事务。

恢复事务的有关源码:

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager {implements ResourceTransactionManager, InitializingBean {private void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {//恢复事务resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());}}protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)throws TransactionException {if (resourcesHolder != null) {Object suspendedResources = resourcesHolder.suspendedResources;if (suspendedResources != null) {//恢复旧连接,绑定至线程doResume(transaction, suspendedResources);}//恢复事务状态和信息List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;if (suspendedSynchronizations != null) {TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);doResumeSynchronization(suspendedSynchronizations);}}}
}public class DataSourceTransactionManager extends AbstractPlatformTransactionManagerimplements ResourceTransactionManager, InitializingBean {protected void doResume(Object transaction, Object suspendedResources) {ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;//重新将原数据库连接绑定到线程TransactionSynchronizationManager.bindResource(this.dataSource, conHolder);}}

下面来看看事务挂起和恢复的详细过程。假设有代理对象A和代理对象B:

  • 代理对象A的注解为@Transactional
  • 代理对象B的注解为@Transactional(propagation = Propagation.REQUIRES_NEW)

当调用代理对象A的方法a()时,方法a()嵌套调用了代理对象B的方法b(),这时候会产生事务的挂起和恢复。

挂起和恢复事务的时序图:

在这里插入图片描述


提交事务

Spring提交通过事务管理器提交事务,通过事务对象txObject内含的数据库连接提交事务,在提交事务完成后将做下列操作:

  1. 事务状态设置为已完成。
  2. 解绑线程的数据库连接。
  3. 清除线程绑定的事务状态和信息。
  4. 重置为旧事务信息TransactionInfo(方法嵌套调用的时候)。
  5. 如果事务状态为新事务,将数据库连接归还连接池。

事务提交调用栈

在这里插入图片描述

源码:

//事务拦截器
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor//该方法继承子类TransactionAspectSupportprotected void commitTransactionAfterReturning(TransactionInfo txInfo) {if (txInfo != null && txInfo.hasTransaction()) {//通过事务管理器提交事务txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}protected void cleanupTransactionInfo(TransactionInfo txInfo) {if (txInfo != null) {//恢复为旧的事务信息(方法嵌套调用会还原为调用栈中上一个方法的事务信息)txInfo.restoreThreadLocalStatus();}}protected final class TransactionInfo {private void restoreThreadLocalStatus() {//恢复为旧的事务信息(方法嵌套调用会还原为调用栈中上一个方法的事务信息)transactionInfoHolder.set(this.oldTransactionInfo);}}//省略代码……}//事务管理器
public class DataSourceTransactionManager extends AbstractPlatformTransactionManagerpublic final void commit(TransactionStatus status) throws TransactionException {//如果事务状态为已完成,抛出异常if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;if (defStatus.isLocalRollbackOnly()) {processRollback(defStatus);return;}if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {processRollback(defStatus);if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}return;}processCommit(defStatus);  //处理事务提交}//该方法继承子类AbstractPlatformTransactionManagerprivate void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {prepareForCommit(status);//调用业务方法里注册TransactionSynchronization对象的beforeCommit()方法triggerBeforeCommit(status);//调用业务方法里注册TransactionSynchronization对象的beforeCompletion()方法triggerBeforeCompletion(status);   beforeCompletionInvoked = true;boolean globalRollbackOnly = false;if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {globalRollbackOnly = status.isGlobalRollbackOnly();}//释放在当前事务中之前定义的一个保存点。if (status.hasSavepoint()) {status.releaseHeldSavepoint();}else if (status.isNewTransaction()) {//如果是事务状态为新事务才提交(方法嵌套调用的话是在最外层的方法)doCommit(status);}// Throw UnexpectedRollbackException if we have a global rollback-only// marker but still didn't get a corresponding exception from commit.if (globalRollbackOnly) {throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");}}catch (UnexpectedRollbackException ex) {//调用业务方法里注册TransactionSynchronization对象的afterCompletion()方法triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// can only be caused by doCommitif (isRollbackOnCommitFailure()) {doRollbackOnCommitException(status, ex);}else {//调用业务方法里注册TransactionSynchronization对象的afterCompletion()方法triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);}throw ex;}catch (RuntimeException ex) {if (!beforeCompletionInvoked) {//调用业务方法里注册TransactionSynchronization对象的beforeCompletion()方法triggerBeforeCompletion(status);}doRollbackOnCommitException(status, ex);throw ex;}catch (Error err) {if (!beforeCompletionInvoked) {//调用业务方法里注册TransactionSynchronization对象的beforeCompletion()方法triggerBeforeCompletion(status);}doRollbackOnCommitException(status, err);throw err;}try {//调用业务方法里注册TransactionSynchronization对象的afterCommit()方法triggerAfterCommit(status);}finally {//调用业务方法里注册TransactionSynchronization对象的afterCommit()方法triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}}finally {cleanupAfterCompletion(status);}}protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();try {con.commit();  //使用数据库连接提交事务}catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);}}private void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();   //事务状态设置为已完成if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {//如果是嵌套方法,恢复原来挂起的事务resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());}}protected void doCleanupAfterCompletion(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;//解绑线程已绑定的数据库连接if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.unbindResource(this.dataSource);}//重置数据库连接的设置Connection con = txObject.getConnectionHolder().getConnection();try {if (txObject.isMustRestoreAutoCommit()) {con.setAutoCommit(true);}DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());}catch (Throwable ex) {logger.debug("Could not reset JDBC Connection after transaction", ex);}if (txObject.isNewConnectionHolder()) {//返还数据库连接DataSourceUtils.releaseConnection(con, this.dataSource);}txObject.getConnectionHolder().clear();}}

回滚事务

在业务逻辑执行过程中出现异常时,事务拦截器会捕获异常,并根据事务注解的配置判断是否符合回滚的条件,若符合则会回滚事务。

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {if (txInfo.transactionAttribute.rollbackOn(ex)) {  //根据规则判断是否要回滚try {//使用事务管理器回滚事务txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException ex2) {throw ex2;}catch (Error err) {throw err;}}else {//使用事务管理器提交事务try {txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException ex2) {throw ex2;}catch (Error err) {logger.error("Application exception overridden by commit error", ex);throw err;}}}}private void processRollback(DefaultTransactionStatus status) {try {try {triggerBeforeCompletion(status);if (status.hasSavepoint()) {status.rollbackToHeldSavepoint();}else if (status.isNewTransaction()) {//如果事务状态为新事务才回滚(比如嵌套调用的最外层方法)doRollback(status);}else if (status.hasTransaction()) {if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {doSetRollbackOnly(status);}}}catch (RuntimeException ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw ex;}catch (Error err) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw err;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);}finally {cleanupAfterCompletion(status);}}protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();try {con.rollback();  //使用数据库连接回滚事务}catch (SQLException ex) {throw new TransactionSystemException("Could not roll back JDBC transaction", ex);}
}

回滚规则的判断

判断当前异常是否符合注解@Transactional设置的rollbackFor和noRollbackFor,只有符合条件的异常才回滚事务。

public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute {public boolean rollbackOn(Throwable ex) {RollbackRuleAttribute winner = null;int deepest = Integer.MAX_VALUE;//是否命中回滚规则if (this.rollbackRules != null) {for (RollbackRuleAttribute rule : this.rollbackRules) {int depth = rule.getDepth(ex);if (depth >= 0 && depth < deepest) {deepest = depth;winner = rule;}}}//如果没有命中回滚规则,使用父类的默认回滚规则if (winner == null) {return super.rollbackOn(ex);}//不是非回滚规则return !(winner instanceof NoRollbackRuleAttribute);}//该方法继承自DefaultTransactionAttribute,代码在DefaultTransactionAttributepublic boolean rollbackOn(Throwable ex) {//默认回滚的异常类型:RuntimeException和Errorreturn (ex instanceof RuntimeException || ex instanceof Error);}
}

从源码可知默认回滚的异常类型为RuntimeExceptionError,当出现IOExceptionNoClassDefFoundException之类的非RuntimeException异常时,默认情况事务并不会回滚,这可能会导致和我们预期的不一致。大多数情况下,推荐事务注解配置rollbackFor的回滚异常为Exception.class。

@Transactional(rollbackFor = Exception.class)

事务完成后清理的方法

Spring在事务提交或回滚后使用cleanupAfterCompletion()方法在事务完成后执行一些必要的清理操作,以确保下一个事务可以从一个干净的状态开始。

//事务管理器
public class DataSourceTransactionManager extends AbstractPlatformTransactionManagerprivate void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();   //事务状态设置为已完成if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {//如果是嵌套方法,恢复原来挂起的事务resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());}}protected void doCleanupAfterCompletion(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;//解绑线程已绑定的数据库连接if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.unbindResource(this.dataSource);}//重置数据库连接的设置Connection con = txObject.getConnectionHolder().getConnection();try {if (txObject.isMustRestoreAutoCommit()) {con.setAutoCommit(true);}DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());}catch (Throwable ex) {logger.debug("Could not reset JDBC Connection after transaction", ex);}if (txObject.isNewConnectionHolder()) {//返还数据库连接DataSourceUtils.releaseConnection(con, this.dataSource);}txObject.getConnectionHolder().clear();}}

事务同步管理器TransactionSynchronizationManager

Spring 的事务同步管理器 TransactionSynchronizationManager 是 Spring 框架中用于管理事务同步的工具类。它使用ThreadLocal存储信息,允许在事务的不同阶段注册回调,以执行特定的操作。主要作用包括以下几个方面:

事务同步回调注册: TransactionSynchronizationManager允许在事务的不同阶段注册回调方法,如在事务完成时、事务回滚时、事务提交前等。开发者可以通过该工具类注册相应的回调方法来执行特定的操作,例如在事务提交后清理资源、发送消息等。

线程绑定: TransactionSynchronizationManager是基于线程的,它允许将事务同步回调与当前线程进行绑定。这意味着只有与事务关联的线程才能触发事务同步回调,从而保证了线程安全性。

支持多个同步回调: TransactionSynchronizationManager支持注册多个事务同步回调,这些回调可以按照注册的顺序执行。这样可以方便地实现多个模块间的协作,每个模块都可以在适当的时候执行自己的逻辑。

提供事务状态管理: TransactionSynchronizationManager还提供了方法来获取当前事务的状态,如事务是否处于激活状态、是否已经完成、是否已经回滚等。这样可以帮助开发者根据当前事务的状态执行相应的操作。

与事务管理器配合使用: TransactionSynchronizationManager 通常与事务管理器(如 PlatformTransactionManager)配合使用,以确保事务同步回调的正确执行。它可以在事务的开始、提交、回滚等关键节点触发相应的回调,并在合适的时机执行事务同步逻辑。

线程绑定的信息

public abstract class TransactionSynchronizationManager {//数据库连接持有者connectionHolderprivate static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<Map<Object, Object>>("Transactional resources");//在业务方法里通过TransactionSynchronizationManager#registerSynchronization注册的同步执行方法private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");//业务名称private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<String>("Current transaction name");//事务是否只读private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<Boolean>("Current transaction read-only status");//业务隔离级别private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<Integer>("Current transaction isolation level");//事务是否激活private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<Boolean>("Actual transaction active");}

绑定和解绑资源

这里的资源是指数据库连接持有者。

	public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");Map<Object, Object> map = resources.get();//如果绑定的map对象为空,初始化它if (map == null) {map = new HashMap<Object, Object>();resources.set(map);}Object oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}}public static Object unbindResource(Object key) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Object value = doUnbindResource(actualKey);if (value == null) {throw new IllegalStateException("No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}return value;}private static Object doUnbindResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.remove(actualKey);if (map.isEmpty()) {resources.remove();  //清除线程绑定的数据库连接}//连接为无效时将它设置为空if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {value = null;}return value;}

事务失效

在Spring出现事务失效最常见的是因为在同一个类内部的方法之间的调用,在上面已经分析过只有在使用代理对象调用业务方法才会有对应的功能增强,同一个类内方法间用的是this对象,不具备事务增强。

下面是一个失效例子:

@Service
public class Caller {@AutowiredSimpleService simpleService;public void call() {simpleService.updateA();}
}@Service
public class SimpleService {public void updateA() {//这里调用updateB()等同于this.updateB(),使用的是this对象,不具备事务增强,事务失效updateB();}@Transactional(rollbackFor = Exception.class)public void updateB() {String sql = "update article set title = 'Today is a good day!' where id = 1";jdbcTemplate.update(sql);}
}

小结

本文简述了Spring注解式事务@Transactional的原理,分析了关键代码,了解源码的实现能帮助我们更好的使用。

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

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

相关文章

【C++从0到王者】第五十站:B树

文章目录 一、内查找与外查找1.内查找2.外查找 二、B树概念三、B树的插入1.B树的插入分析2.B树插入总结3.插入代码实现4.B树满树和最空时候的对比5.B树的删除6.遍历B树7.B树的性能分析 一、内查找与外查找 1.内查找 像我们之前所用的在内存中的查找就是内查找 种类数据格式时…

IEEE754标准的c语言阐述,以及几个浮点数常量

很多年前&#xff0c;调研过浮点数与整数之间的双射问题&#xff1a; win7 intel x64 cpu vs2013 c语言浮点数精度失真问题 最近重新学习了一下IEEE754标准&#xff0c;也许实际还有很多深刻问题没有被揭示。 计算机程序设计艺术&#xff0c;据说这本书中也有讨论。 参考&…

如何将字体添加到 ONLYOFFICE 桌面编辑器8.0

作者&#xff1a;VincentYoung 为你写好的文字挑选一款好看的字体然而自带的字体列表却找不到你喜欢的怎么办&#xff1f;这只需要自己手动安装一款字体即可。这里教你在不同的桌面操作系统里的多种字体安装方法。 ONLYOFFICE 桌面编辑器 ONLYOFFICE 桌面编辑器是一款免费的办…

【C语言】终の指针(前篇)

个人主页点这里~ 指针初阶点这里~ 指针初阶2.0点这里~ 指针进阶点这里~ 终の指针 一、回调函数二、qsort函数1、整形比较2、结构数据比较①结构体②-> 的使用③结构数据比较 一、回调函数 回调函数就是⼀个通过函数指针调用的函数。 把一个函数的指针作为参数传递给另一…

@ResponseStatus

目录 概述&#xff1a; 用途&#xff1a; 参数&#xff1a; 注意事项&#xff1a; 自定义异常类&#xff1a; 底层原理&#xff1a; 概述&#xff1a; 在 Spring MVC 中&#xff0c;我们有很多方法来设置 HTTP 响应的状态码其中最直接的方法&#xff1a;使用 ResponseSt…

思维题(蓝桥杯 填空题 C++)

目录 题目一&#xff1a; ​编辑 代码&#xff1a; 题目二&#xff1a; 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目五&#xff1a; 代码&#xff1a; 题目六&#xff1a; 代码七&#xff1a; 题目八&#x…

分享关于如何解决系统设计问题的逐步框架

公司广泛采用系统设计面试&#xff0c;因为在这些面试中测试的沟通和解决问题的技能与软件工程师日常工作所需的技能相似。面试官的评估基于她如何分析一个模糊的问题以及如何逐步解决问题。测试的能力还包括她如何解释这个想法&#xff0c;与他人讨论&#xff0c;以及评估和优…

外包干了2年,技术退步明显

先说一下自己的情况&#xff0c;研究生&#xff0c;19年进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

docker部署若依项目

目录 目录 一、搭建局域 二、redis安装 1.创建目录 2. redis.conf修改 三、MySQL安装 1. 安装 2. 设置远程连接 3. 创建数据库 四、若依后端项目搭建 1. 切换到家目录 2. 上传jar包 3. 上传Dockerfile文件 4. 构建镜像 5. 运行容器 6. 查看运行情况 7. 测试(自己…

mysql bug( InnoDB: Error number 22),表突然不能读取

mysql bug&#xff08; InnoDB: Error number 22&#xff09;&#xff0c;表突然不能读取 bug最开始的bug&#xff1a;表突然不能读取关闭mysql容器&#xff0c;再次重启失败 解决方案不重建容器的几种可能措施重建容器重建如果懒得打命令或者忘记命令可能的run bug&#xff1a…

Hololens 2应用开发系列(2)——MRTK基础知识及配置文件配置(上)

Hololens 2应用开发系列&#xff08;2&#xff09;——MRTK基础知识及配置文件配置 一、前言二、MRTK基础知识2.1 MRTK概述2.2 MRTK运行逻辑2.3 MRTK配置文件介绍2.4 MRTK服务 三、配置文件使用3.1 总配置文件3.2 相机配置3.3 其他配置 参考文献 一、前言 在前面的文章中&…

【智能家居入门1之环境信息监测】(STM32、ONENET云平台、微信小程序、HTTP协议)

作为入门本篇只实现微信小程序接收下位机上传的数据&#xff0c;之后会持续发布如下项目&#xff1a;①可以实现微信小程序控制下位机动作&#xff0c;真正意义上的智能家居&#xff1b;②将网络通讯协议换成MQTT协议再实现上述功能&#xff0c;此时的服务器也不再是ONENET&…

一文看懂:组件化设计,B端系统不再重复造轮子。

B端组件化设计是指将企业级系统的界面设计和开发分解为独立的可复用组件&#xff0c;以实现系统的模块化和灵活性。 B端组件化设计的核心思想是将界面拆分成独立的功能组件&#xff0c;每个组件负责特定的功能或业务逻辑&#xff0c;可以独立开发、测试和维护&#xff0c;同时可…

[HackMyVM]靶场 Wild

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 …

综合练习

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 现有一个商店的数据库&#xff0c;记录顾客及其购物情况。根据要求完成任务 此数据库由下面 3 个表组成。 商品 product&#xff08;商品号 productid&#xff0c;商品名 p…

交友盲盒系统PHP开源的盲盒源码

源码介绍&#xff1a; 交友盲盒系统是一款基于PHP开发的开源免费盲盒系统&#xff0c;旨在为用户提供一个充满乐趣和惊喜的社交体验。该系统具有丰富的功能和灵活的扩展性&#xff0c;可以轻松地满足各种线上交友、抽奖活动等场景的需求。 安装说明&#xff1a; PHP版本&…

海外IP代理应用:亚马逊使用什么代理IP?

代理IP作为网络活动的有力工具&#xff0c;同时也是跨境电商的必备神器。亚马逊作为跨境电商的头部平台&#xff0c;吸引了大量的跨境电商玩家入驻&#xff0c;想要做好亚马逊&#xff0c;养号、测评都需要代理IP的帮助。那么应该使用什么代理IP呢&#xff1f;如何使用&#xf…

LeetCode 刷题 [C++] 第300题.最长递增子序列

题目描述 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 题目…

【学习笔记】卫星基础知识

一、什么是卫星以及它如何工作&#xff1f; 通信卫星是一种人造卫星&#xff0c;通过使用转发器在源和接收器之间中继和放大无线电电信信号。卫星的工作原理是接收从地球发送的无线电信号并将无线电信号重新发送回地球。卫星使用从大型太阳能电池阵列收集的太阳能&#xff0c;…

npm ERR! code ERR_INVALID_URL报错解决

这个报错是URL错误&#xff0c;要排除两个点 npm的registry有没有搞错&#xff0c;也就是npm源有没有搞错 打开文件C:/User/<用户名>/.npmrc查看npm设置查看registry的设置有没有格式错误正确设置格式&#xff1a;registry"https://registry.npmmirror.com"或…