SpringBoot 自动事务管理原理

Spring 自动事务管理的实现

SpringBoot @Transactional 注解未生效 阐述了Spring 自动事务管理的实现基础为 AOP,而 Spring 对事务的处理流程可概括为以下几步:

  1. 获取事务属性,即解析@Transactional注解中的参数
  2. 获取事务管理器,就是在@Transactional注解中的transactionManager参数
  3. 收集事务信息,当前线程的事务状态,开启事务,并在当前线程中设置事务相关参数,标注当前线程是运行在事务中的
  4. 执行被拦截的方法业务代码,即被@Transactional注解标注的方法
  5. 如果方法执行出现异常,做相关处理,可能会执行回滚操作或者设置回滚标志位
  6. 清理事务环境,将设置到Threadlocal中的相关变量清除
  7. 提交事务

在具体分析时,结合 SpringBoot 的自动配置机制,可以将 Spring 事务的实现分为两个部分,即事务注解 Bean 的创建事务执行的流程
在这里插入图片描述

1. 事务注解 Bean 的创建

  1. 事务自动配置类TransactionAutoConfiguration在程序启动时会被加载,其内部类 EnableTransactionManagementConfiguration启用了注解@EnableTransactionManagement,从而开启了注解事务自动管理

    public static class EnableTransactionManagementConfiguration {
    
     	@Configuration
     	@EnableTransactionManagement(proxyTargetClass = false)
     	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
     	public static class JdkDynamicAutoProxyConfiguration {
    
     	}
    
     	@Configuration
     	@EnableTransactionManagement(proxyTargetClass = true)
     	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
     	public static class CglibAutoProxyConfiguration {
    
     	}
    
     }
    
  2. @EnableTransactionManagement 通过@Import(TransactionManagementConfigurationSelector.class) 导入了配置选择者 TransactionManagementConfigurationSelector

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(TransactionManagementConfigurationSelector.class)
    public @interface EnableTransactionManagement {
    
        boolean proxyTargetClass() default false;
        
        AdviceMode mode() default AdviceMode.PROXY;
        
        int order() default Ordered.LOWEST_PRECEDENCE;
    }
    
  3. TransactionManagementConfigurationSelector#selectImports() 根据 @EnableTransactionManagement 配置的增强模式 AdviceMode 来决定使用哪种事务管理配置类,通常默认采用 PROXY代理模式。以代理模式为例,容器启动过程中会通过配置的 注册者AutoProxyRegistrarProxyTransactionManagementConfiguration 注册到容器中,从而使配置生效

    public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    
     /**
      * Returns {@link ProxyTransactionManagementConfiguration} or
      * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
      * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
      * respectively.
      */
     @Override
     protected 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;
     	}
     }
    ......
    
    }
    
    
  4. ProxyTransactionManagementConfiguration 作为代理模式事务管理的配置类,其内配置了对事务管理举足轻重的三个 bean。其中 BeanFactoryTransactionAttributeSourceAdvisor 作为事务增强的切面,切面内包含了TransactionInterceptor 实例作为增强,而 AnnotationTransactionAttributeSource 实例则是用于生成匹配目标 bean 的切入点

    @Configuration
    public class ProxyTransactionManagementConfiguration extends  AbstractTransactionManagementConfiguration {
    
     @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
     public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
     	BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
     	advisor.setTransactionAttributeSource(transactionAttributeSource());
     	advisor.setAdvice(transactionInterceptor());
     	if (this.enableTx != null) {
     		advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
     	}
     	return advisor;
     }
    
     @Bean
     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
     public TransactionAttributeSource transactionAttributeSource() {
     	return new AnnotationTransactionAttributeSource();
     }
    
     @Bean
     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
     public TransactionInterceptor transactionInterceptor() {
     	TransactionInterceptor interceptor = new TransactionInterceptor();
     	interceptor.setTransactionAttributeSource(transactionAttributeSource());
     	if (this.txManager != null) {
     		interceptor.setTransactionManager(this.txManager);
     	}
     	return interceptor;
     }
    
    }
    
  5. BeanFactoryTransactionAttributeSourceAdvisor 内部保存了切入点成员变量 pointcut,可以看到该成员变量实例为 TransactionAttributeSourcePointcut,这个切入点变量的作用就是匹配目标 bean

    public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    
    @Nullable
    private TransactionAttributeSource transactionAttributeSource;
    
    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    	@Override
    	@Nullable
    	protected TransactionAttributeSource getTransactionAttributeSource() {
    		return transactionAttributeSource;
    	}
    };
    
    
    /**
     * Set the transaction attribute source which is used to find transaction
     * attributes. This should usually be identical to the source reference
     * set on the transaction interceptor itself.
     * @see TransactionInterceptor#setTransactionAttributeSource
     */
    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
    	this.transactionAttributeSource = transactionAttributeSource;
    }
    
    /**
     * Set the {@link ClassFilter} to use for this pointcut.
     * Default is {@link ClassFilter#TRUE}.
     */
    public void setClassFilter(ClassFilter classFilter) {
    	this.pointcut.setClassFilter(classFilter);
    }
    
    @Override
    public Pointcut getPointcut() {
    	return this.pointcut;
    }
    
    }
    
    
  6. 通过以上步骤事务方法的切面就配置完成了,接下来就是切面与目标 bean 的匹配。这部分详细流程参考 Spring AOP 创建代理对象源码分析 可知匹配的最终过程是在 AopUtils#canApply()方法中完成的,此处以事务增强的例子着重分析具体的匹配过程。首先进入 AopUtils#canApply(),可看到该方法中首先通过 pc.getMethodMatcher() 获取到切入点中的方法匹配者MethodMatcher对象,之后调用其 matches() 方法将切面中的增强方法与目标 bean 进行匹配

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
     ......
    
     MethodMatcher methodMatcher = pc.getMethodMatcher();
     
     ......
     
     for (Class<?> clazz : classes) {
     	Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
     	for (Method method : methods) {
     		if (introductionAwareMethodMatcher != null ?
     				introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
     				methodMatcher.matches(method, targetClass)) {
     			return true;
     		}
     	}
     }
     return false;
     }
    
  7. 步骤5 可知,事务增强的切入点对象实例为 TransactionAttributeSourcePointcut,查看其matchs()方法实现,可知其根据 TransactionAttributeSource#getTransactionAttribute() 方法去获取事务属性,如果不为空即可匹配成功。而从 步骤4 又可知TransactionAttributeSource的实现实例为 AnnotationTransactionAttributeSource

    @Override
     public boolean matches(Method method, Class<?> targetClass) {
     	if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
     			PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
     			PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
     		return false;
     	}
     	TransactionAttributeSource tas = getTransactionAttributeSource();
     	return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
     }
    
  8. AnnotationTransactionAttributeSource 继承自AbstractFallbackTransactionAttributeSourcegetTransactionAttribute()方法的实现其实是在AbstractFallbackTransactionAttributeSource 中,其内部会调用computeTransactionAttribute()方法去计算解析事务属性,这个方法的核心为调用 findTransactionAttribute()需注意方法内会判断目标方法的访问修饰符,如果不是public 直接返回 null

    public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
     	if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
     		return null;
     	}
     	......
     		// We need to work it out.
     		TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
     	......
     		return txAttr;
     	}
     }
    
  9. findTransactionAttribute()方法的实现在 AnnotationTransactionAttributeSource 中,其核心流程为调用内部的determineTransactionAttribute()方法解析目标方法,核心的解析类为 TransactionAnnotationParser接口的子类

    protected TransactionAttribute findTransactionAttribute(Method method) {
     	return determineTransactionAttribute(method);
     }
    
    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
     	for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
     		TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
     		if (attr != null) {
     			return attr;
     		}
     	}
     	return null;
     }
    
  10. TransactionAnnotationParser接口的实现类有 3 个,其中以@Transactional注解的事务方法由 SpringTransactionAnnotationParser#parseTransactionAnnotation() 解析,经过该步骤会将@Transactional注解属性解析为事务属性 TransactionAttribute至此,根据是否能够从目标方法上解析出事务属性,就能够判断事务切面与目标 bean 是否匹配,从而决定是否要创建目标 bean 的事务代理对象

    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    	AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
    			element, Transactional.class, false, false);
    	if (attributes != null) {
    		return parseTransactionAnnotation(attributes);
    	}
    	else {
    		return null;
    	}
    }
    

2. 事务执行的流程

2.1 事务拦截器拦截方法的触发

  1. 事务注解的 bean 的代理对象被创建后,事务方法被调用时则触发 aop 方法拦截, DynamicAdvisedInterceptor#intercept()方法被调用,需注意如果方法的拦截链为空(类内部调用)并且方法是 public 的则不执行代理类的增强方法,也就不会有事务效果。不满足该条件则先创建 CglibMethodInvocation 对象,并调用对象的proceed()方法

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
     		Object oldProxy = null;
     		boolean setProxyContext = false;
     		Object target = null;
     		TargetSource targetSource = this.advised.getTargetSource();
     		try {
     			if (this.advised.exposeProxy) {
     				// Make invocation available if necessary.
     				oldProxy = AopContext.setCurrentProxy(proxy);
     				setProxyContext = true;
     			}
     			// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
     			target = targetSource.getTarget();
     			Class<?> targetClass = (target != null ? target.getClass() : null);
     			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
     			Object retVal;
     			// Check whether we only have one InvokerInterceptor: that is,
     			// no real advice, but just reflective invocation of the target.
     			if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
     				// We can skip creating a MethodInvocation: just invoke the target directly.
     				// Note that the final invoker must be an InvokerInterceptor, so we know
     				// it does nothing but a reflective operation on the target, and no hot
     				// swapping or fancy proxying.
     				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
     				retVal = methodProxy.invoke(target, argsToUse);
     			}
     			else {
     				// We need to create a method invocation...
     				retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
     			}
     			retVal = processReturnType(proxy, target, method, retVal);
     			return retVal;
     		}
     		finally {
     			if (target != null && !targetSource.isStatic()) {
     				targetSource.releaseTarget(target);
     			}
     			if (setProxyContext) {
     				// Restore old proxy.
     				AopContext.setCurrentProxy(oldProxy);
     			}
     		}
     	}
    
  2. proceed()方法的实现实际是在 CglibMethodInvocation 的父类ReflectiveMethodInvocation 中。该方法会被递归调用,直到拦截裢最后一个拦截器的拦截方法通过 invokeJoinpoint() 真正执行,递归开始收束,整个拦截链上的拦截方法都会被执行。对于事务场景,此处调用事务拦截器TransactionInterceptor#invoke() 方法

     public Object proceed() throws Throwable {
     	
     	......
     	
     	else {
     		// It's an interceptor, so we just invoke it: The pointcut will have
     		// been evaluated statically before this object was constructed.
     		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
     	}
     }
    
  3. TransactionInterceptor#invoke() 方法其实只是调用了其父类TransactionAspectSupportinvokeWithinTransaction()方法,这个方法中执行@Transcational修饰的方法的事务操作

    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
     		final InvocationCallback invocation) throws Throwable {
    
     	// If the transaction attribute is null, the method is non-transactional.
     	// 1. 获取事务的属性
     	TransactionAttributeSource tas = getTransactionAttributeSource();
     	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
     	// 2. 获取事务管理器
     	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
     	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
         // Spring 自动管理的声明式事务
     	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
     		// Standard transaction demarcation with getTransaction and commit/rollback calls.
     		
             // 3. 解析事务属性,状态,开启事务,在Threadlocal中设置相关变量
     		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    
     		Object retVal;
     		try {
     			// This is an around advice: Invoke the next interceptor in the chain.
     			// This will normally result in a target object being invoked.
     			// 4. 执行被@Transactional注解标注的业务方法
     			retVal = invocation.proceedWithInvocation();
     		}
     		catch (Throwable ex) {
     			// target invocation exception
     			// 5. 如果允许出现异常,执行处理,如回滚操作
     			completeTransactionAfterThrowing(txInfo, ex);
     			throw ex;
     		}
     		finally {
     		    // 6.清理事务环境,将第3步中 ThreadLocal 中设置的变量清除
     			cleanupTransactionInfo(txInfo);
     		}
     		// 7.提交事务
     		commitTransactionAfterReturning(txInfo);
     		return retVal;
     	}
        // 手动管理的事务
     	else {
     		......
     		}
     }
    

2.2 事务执行的详细流程

2.2.1 获取事务属性

tas.getTransactionAttribute()方法获取事务属性 TransactionAttribute,该部分与事务 bean 创建中的步骤8完全相同。通常情况下,在 bean 创建的时候已经扫描@Transactional然后把属性存到缓存中,因此这里会从缓存里取,这样不用每次调用这个方法时都去解析一遍

2.2.2 获取事务管理器

determineTransactionManager()方法获取PlatformTransactionManager 事务管理器。事务管理器保存着当前的数据源DataSource,一个事务管理器对应一个数据源,它对外提供对该数据源的事务提交回滚等操作接口,以其子类实现 DataSourceTransactionManager为例,其中提供了许多事务相关的操作方法。事务管理器的获取有两种途径:[1].首先从注解中解析获得 [2].如果注解中没有标明,则从容器中找到一个实现了PlatformTransactionManager接口的Bean,如果这种接口的bean有多个,同时没有使用@Primary标注的话,就会报错,具体可参考 DefaultListableBeanFactory#resolveNamedBean()方法的实现。所以在写@Transactional注解时尽量指定 transactionManager 参数,以便开启指定数据库的事务,否则当程序中有多个数据源时,如果使用了错误的事务管理器,事务显然是不会生效的

protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
 	// Do not attempt to lookup tx manager if no tx attributes are set
 	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 {
 		PlatformTransactionManager defaultTransactionManager = getTransactionManager();
 		if (defaultTransactionManager == null) {
 			defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
 			if (defaultTransactionManager == null) {
 				defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
 				this.transactionManagerCache.putIfAbsent(
 						DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
 			}
 		}
 		return defaultTransactionManager;
 	}
 }

2.2.3 开启事务

  1. createTransactionIfNecessary() 方法是事务开启的入口,其核心为调用 tm.getTransaction() 去开启事务

    protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
    		@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    
    	......
    
    	TransactionStatus status = null;
    	if (txAttr != null) {
    		if (tm != null) {
    			status = tm.getTransaction(txAttr);
    		}
    		else {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
    						"] because no transaction manager has been configured");
    			}
    		}
    	}
    	return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    }
    
  2. tm.getTransaction() 方法有两种实现,此处以AbstractPlatformTransactionManager#getTransaction()分析,可以看到其大致处理流程:

    [1]. 获得事务对象DataSourceTransactionObject 
    [2]. 判断当前线程是否已经在事务中 
    [3]. 如果已经在一个事务中,则调用 handleExistingTransaction,根据不同的传播级别进行不同的操作 
    [4]. 如果不在一个事务中,则新建事务状态,并开启事务
    
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
     	// 1. 获取事务对象
     	Object transaction = doGetTransaction();
     	
     	.....
     	
     	// 2. 判断是否已经存在事务
     	if (isExistingTransaction(transaction)) {
     		// Existing transaction found -> check propagation behavior to find out how to behave.
     		// 3. 已经开启事务,根据不同传播级别执行不同操作
     		return handleExistingTransaction(definition, transaction, debugEnabled);
     	}
    
     	......
     	// No existing transaction found -> check propagation behavior to find out how to proceed.
     	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_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);
     			// 3. 新建事务状态
     			DefaultTransactionStatus status = newTransactionStatus(
     					definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
     			// 4. 开启事务
     			doBegin(transaction, definition);
     			prepareSynchronization(status, definition);
     			return status;
     		}
     		catch (RuntimeException | Error ex) {
     			resume(null, suspendedResources);
     			throw ex;
     		}
     	}
     	......
     }
    
  3. doGetTransaction() 的实现以DataSourceTransactionManager#doGetTransaction()为例,可以看到其方法体首先新建了数据源事务对象 DataSourceTransactionObject,之后以当前数据源 DataSource 为 keyTransactionSynchronizationManager中获取到数据源的数据库连接,并将其设置到数据源事务对象中。需注意TransactionSynchronizationManager这个类是每个线程事务同步管理的代理中心,用来管理事务用到的相关资源

    protected Object doGetTransaction() {
     	DataSourceTransactionObject txObject = new DataSourceTransactionObject();
     	txObject.setSavepointAllowed(isNestedTransactionAllowed());
     	ConnectionHolder conHolder =
     			(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
     	txObject.setConnectionHolder(conHolder, false);
     	return txObject;
     }
    
  4. DataSourceTransactionManager#isExistingTransaction() 判断当前线程是否已经存在事务的逻辑很简单,就是通过数据源事务对象 DataSourceTransactionObject获取其数据库连接,判断该连接上的标识位 transactionActive 是否为 true

    protected boolean isExistingTransaction(Object transaction) {
     	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
     	return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
     }
    
  5. 如果当前线程已经有事务存在了,就需要调用AbstractPlatformTransactionManager#handleExistingTransaction()处理事务传播问题。以传播级别PROPAGATION_REQUIRES_NEW 为例,可以看到 Spring 会执行一个suspend()操作清空线程中保存的当前事务属性并将其存储到 SuspendedResourcesHolder 对象中用于异常恢复,然后创建一个新的TransactionStatus对象用于doBegin()开启事务,整个过程也就是挂起当前事务开启一个新事务。之后对于REQUIRED的传播级别,则是直接返回,没有挂起和doBegin()的操作。

    private TransactionStatus handleExistingTransaction(
     		TransactionDefinition definition, Object transaction, boolean debugEnabled)
     		throws TransactionException {
    
     	......
     	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
     		
     		......
         
             // 挂起事务
     		SuspendedResourcesHolder suspendedResources = suspend(transaction);
     		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 | Error beginEx) {
     			resumeAfterBeginException(transaction, suspendedResources, beginEx);
     			throw beginEx;
     		}
     	}
    
        ......
        // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
        ......
     	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
     	return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
     }
    
  6. 如果当前线程不存在事务,就调用AbstractPlatformTransactionManager#newTransactionStatus()方法新建DefaultTransactionStatus事务状态对象,再调用doBegin()开启事务。这里可以看到开启事务的具体操作con.setAutoCommit(false);,其实就是原生JDBC开启事务的方式,获取数据库连接再将其自动提交关闭

    protected void doBegin(Object transaction, TransactionDefinition definition) {
     	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
     	Connection con = null;
    
     	......
     	    // 获取数据库连接
     		con = txObject.getConnectionHolder().getConnection();
    
     		Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
     		txObject.setPreviousIsolationLevel(previousIsolationLevel);
    
     		// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
     		// so we don't want to do it unnecessarily (for example if we've explicitly
     		// configured the connection pool to set it already).
     		if (con.getAutoCommit()) {
     			txObject.setMustRestoreAutoCommit(true);
     			if (logger.isDebugEnabled()) {
     				logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
     			}
     			// 开启数据库事务
     			con.setAutoCommit(false);
     		}
    
     		prepareTransactionalConnection(con, definition);
     		txObject.getConnectionHolder().setTransactionActive(true);
     		......
     		
     }
    

2.2.4 异常回滚事务及清理事务信息

  1. 回到TransactionAspectSupport#invokeWithinTransaction()事务主流程, @Transactionl注解的业务方法执行之后,就需要根据方法的执行结果来进行事务的提交或回滚。当业务方法发生异常时,Spring 会捕获该异常,并调用TransactionAspectSupport#completeTransactionAfterThrowing()方法来决定是否回滚事务。可以看到txInfo.transactionAttribute.rollbackOn(ex)决定来是否要进入回滚流程,真正的回滚操作由 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()) 完成

    protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
     	if (txInfo != null && txInfo.getTransactionStatus() != null) {
     		
     		if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
     			try {
     				txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
     			}
     			catch (TransactionSystemException ex2) {
     				logger.error("Application exception overridden by rollback exception", ex);
     				ex2.initApplicationException(ex);
     				throw ex2;
     			}
     			catch (RuntimeException | Error ex2) {
     				logger.error("Application exception overridden by rollback exception", ex);
     				throw ex2;
     			}
     		}
     		else {
     			// We don't roll back on this exception.
     			// Will still roll back if TransactionStatus.isRollbackOnly() is true.
     			try {
     				txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
     			}
     			......
     		}
     	}
     }
    
  2. txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()) 实际调用到了AbstractPlatformTransactionManager#rollback() 方法,其核心为调用 processRollback()方法,总共有三种情况:

    1、如果有保存点会回滚到保存点,这种对应着 NESTED 传播级别,利用了mysql的savepoint来实现
    2、如果是一个新的事务,则真正执行回滚操作
    3、如果是加入一个其他事务的话,则设置回滚标志位,交由父事务执行回滚操作
    

    据此可以知道 Spring 会通过回滚标志位来影响事务的提交和回滚。有时候方法并没有抛出异常也会回滚,比如对于 REQUIRED 传播级别,A方法调用B方法B方法抛出异常后设置了回滚标志位,若A方法捕获此异常并不再抛出异常,看起来A方法会正常执行commit()步骤,其实由于回滚标志位的存在,A方法对应的事务依旧会回滚

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
     	try {
     		boolean unexpectedRollback = unexpected;
    
     		try {
     			triggerBeforeCompletion(status);
                 // 如果有保存点,则回滚到保存点
     			if (status.hasSavepoint()) {
     				if (status.isDebug()) {
     					logger.debug("Rolling back transaction to savepoint");
     				}
     				status.rollbackToHeldSavepoint();
     			}
     			// 如果是新建事务,则直接执行回滚操作
     			else if (status.isNewTransaction()) {
     				if (status.isDebug()) {
     					logger.debug("Initiating transaction rollback");
     				}
     				doRollback(status);
     			}
     			// 以上情况都不符,则是加入了其他的事务,这时不执行回滚操作,而是设置回滚标志位
     			else {
     				// Participating in larger transaction
     				if (status.hasTransaction()) {
     					if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
     						if (status.isDebug()) {
     							logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
     						}
     						doSetRollbackOnly(status);
     					}
     		......
     }
    
  3. 不管事务都执行结果如何,都会在 finally中执行cleanupTransactionInfo(txInfo),将此次事务相关信息从当前线程中清除

2.2.5 正常提交事务

  1. @Transactionl注解的业务方法执行成功,此时需要调用TransactionAspectSupport#commitTransactionAfterReturning()方法进行事务的提交,这个方法的核心是txInfo.getTransactionManager().commit(txInfo.getTransactionStatus())调用事务管理器的提交方法commit()
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}
  1. 与回滚类似,实际调用到了AbstractPlatformTransactionManager#commit() 方法,最终调用到processCommit()方法。此时也并不一定会执行提交操作,而是首先判断是否有savepoint保存点。有保存点,说明是内嵌在其他事务中的子事务,此时释放保存点,不会真正提交。其次判断是否是一个新的事务,如果是新事务直接执行提交。至此,Spring 事务执行流程全部完成
    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
     	try {
     		boolean beforeCompletionInvoked = false;
    
     		try {
     			......
     			// 有保存点,说明是内嵌在其他事务中的子事务,释放保存点,不会真正提交
     			if (status.hasSavepoint()) {
     				if (status.isDebug()) {
     					logger.debug("Releasing transaction savepoint");
     				}
     				unexpectedRollback = status.isGlobalRollbackOnly();
     				status.releaseHeldSavepoint();
     			}
     			// 是新事务直接执行提交
     			else if (status.isNewTransaction()) {
     				if (status.isDebug()) {
     					logger.debug("Initiating transaction commit");
     				}
     				unexpectedRollback = status.isGlobalRollbackOnly();
     				doCommit(status);
     			}
     			else if (isFailEarlyOnGlobalRollbackOnly()) {
     				unexpectedRollback = status.isGlobalRollbackOnly();
     			}
    
     			// Throw UnexpectedRollbackException if we have a global rollback-only
     			// marker but still didn't get a corresponding exception from commit.
     			if (unexpectedRollback) {
     				throw new UnexpectedRollbackException(
     						"Transaction silently rolled back because it has been marked as rollback-only");
     			}
     		}
     		......
     }
    
发布了97 篇原创文章 · 获赞 88 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45505313/article/details/103435630