spring事务源码执行过程分析

上篇博客解析了事务生成动态代理对象的底层代码,简单总结的话就是一句话:如果添加了事务注解@Transactional,且方法是public的,spring就会给该bean生成代理对象,至于是jdk还是cglib,就看自己的事务方法对应的类是否实现了接口
接下来我们来看生成代理对象之后的执行过程

拦截器执行

org.springframework.transaction.interceptor.TransactionInterceptor

我在学习的时候,用的是cglib代理,因为我没有实现接口,所以使用的是CGLIB代理,在目标方法被调用的的时候,是会调用到

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept; 然后会调用到
	org.springframework.transaction.interceptor.TransactionInterceptor#invoke
	 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

这个方法我删减了一部分,区分了声明式事务和编程式事务,这里我们只关注声明式事务

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
		final InvocationCallback invocation) throws Throwable {

	// If the transaction attribute is null, the method is non-transactional.
	TransactionAttributeSource tas = getTransactionAttributeSource();
	//获取事务属性
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	//获取事务管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	//下面是声明式事务的处理逻辑
	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		//看是否有必要创建一个事务,根据事务的传播行为做判断
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
		Object retVal = null;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			//执行目标方法
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			//如果有异常就回滚事务,回滚时,根据配置的rollbackFor进行判断
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			//清除事务信息
			cleanupTransactionInfo(txInfo);
		}
		//提交事务
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
}

这个方法中,

  • 根据事务注解对应的传播机制,判断是否要创建新的事务,还是嵌套在当前事务中运行等;
  • 接着执行目标方法
  • 在执行完目标方法之后,如果未发生异常,则提交事务
  • 如果发生异常,根据配置的rollbackFor和noRollbackFor等判断是否要回滚,如果无需回滚,就提交事务,反之,则回滚事务

因此,我们着重关注两个方法

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
和
completeTransactionAfterThrowing(txInfo, ex);

传播机制源码

传播机制的源码我整理之后再补充

异常回滚源码

如果在执行目标方法的时候,如果发生异常,捕获到之后,会进入到这个方法来处理

/**
 * 这里是判断是否需要回滚的逻辑
 * 如果在事务注解上指定了回滚的异常类型、或者指定了不回滚的异常类型,就会在这里进行判断
 * 1.如果判断满足回滚的条件,就事务回滚
 * 		先判断开发人员指定的类型,如果业务代码抛出的异常符合指定的类型,就回滚
 * 		如果没有指定,就判断是否是错误(error)或者运行时异常(runTimeException),如果是,就回滚
 *    所以:如果程序员没有指定回滚的异常,默认情况下,如果是运行时异常或者是error(错误),也是会进行事务的回滚
 * 2.否则,就提交事务
 * @param txInfo
 * @param ex
 */
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
	if (txInfo != null && txInfo.getTransactionStatus() != null) {
		if (logger.isTraceEnabled()) {
			logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
					"] after exception: " + ex);
		}
		/**
		 * 如果程序员有指定回滚或者不回滚的异常,就会进入
		 * 	org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn(java.lang.Throwable)进行判断
		 *
		 * 如果没有指定,就默认调用org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(java.lang.Throwable)
		 * 进行判断  
		 */
		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 {
      // 这里else就是不符合回滚的条件,会进行事务的提交(即使发生了异常场景)
			// We don't roll back on this exception.
			// Will still roll back if TransactionStatus.isRollbackOnly() is true.
			try {
				txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
			}
			catch (TransactionSystemException ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				ex2.initApplicationException(ex);
				throw ex2;
			}
			catch (RuntimeException | Error ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				throw ex2;
			}
		}
	}
}

这里比较重要的一个方法,就是进行异常的判断,判断当前业务代码抛出的异常,是否符合回滚的要求;

在这里插入图片描述

这里这个方法就是来判断当前抛出的异常是否符合在@Transactional注解上配置的异常回滚信息

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
 
  @Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}
		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;
  	// 这里的rollbackRules是在解析@Transactional注解的时候,保存的;parseTransactionAnnotation在这个方法中解析的注解;这里的rollbackRules既有rollbackException中配置的,也有noRollbackException中配置的
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				/**
				 * depth:当前rule和ex的相似度
				 *
				 * deepest:和ex最相近的depth
				 * winner:相似度最近的RollbackRuleAttribute
				 * 如果当前rule,返回的depth比上一次返回的depth小,且大于0,就用当前这次异常
				 *
				 * 这里会保存和业务代码抛出异常最相似的rule
				 * 比如:
				 * 	我在代码中,抛出了一个java.lang.ArithmeticException: / by zero
				 *
				 * 	如果我在@Transactional(rollbackFor = {Exception.class,ArithmeticException.class})
				 * 	那肯定会返回ArithmeticException这个rule,因为ArithmeticException返回的depth是0
				 * 	Exception返回的的depth是2  下面解释为什么一个是0,一个是2
				 */
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
    // 如果没有匹配到最相似的异常、或者没有配置回滚异常类,就会执行这里,这里是调用父类的方法进行判断
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}
		/**
		 * 如果相似度最近的rule不是无需回滚的类型,就可以进行事务回滚
		 */
		return !(winner instanceof NoRollbackRuleAttribute);
	}
	// 这里就是父类对应的判断逻辑,也就是说:如果程序员单单加了一个@Transactional注解,那么在业务代码抛出运行时异常后者error的时候,还是会回滚的
	@Override
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

那我们接着来看上面判断异常相似度的方法:int depth = rule.getDepth(ex);

这里我称之为相似度,不知道是否合适;这里是根据业务代码中抛出的异常A对应的name和程序员指定的异常B对应的name进行比较,如果匹配上,就返回depth,如果匹配不上,就递归调用,用业务代码中抛出的A异常对应的父类和B异常的name进行比较,依次递归调用

/**
	 * 这里其实就是用业务代码抛出的异常和程序员在@Transactional注解中执行的异常信息进行对比
	 *
	 * 实际底层使用的是String.contains()方法
	 * exceptionName:就是程序员指定的异常对应的类名;比如:我指定的是rollbackFor = Exception.class,那这里的exceptionName就是:java.lang.Exception
	 *
	 * 1.如果当前抛出的异常和程序员指定的异常匹配不上,就依次递归调用抛出异常的父类和程序员指定的异常进行比较,
	 * 		1.1 直到匹配上,就返回当前的depth,depth每递归调用一次,就+1
	 * 		1.2	或者是到Throwable依旧没有比对上,这时,就表示我指定的异常和代码抛出的异常不匹配
	 *
	 * 	这两种场景也好验证:
	 * 		1.首先,我在业务代码中,加上这么一行代码:int i = 10/0;
	 * 		2.然后在@Transactional注解中加上rollbackException = Exception.class	或者是rollbackException = IoException.class
	 * 	    这两种异常,最后事务都会回滚,但是效果却是不一样的
	 * 	    如果我加的是rollbackException = Exception.class,这里会匹配上,返回的是depth是2
	 * 	    但是如果加的是rollbackException = IoException.class,这里返回的是-1
	 *
	 * 	    因为:如果是rollbackException = Exception.class;那这里在匹配的时候,会递归调用两次,
	 * 	    int i = 10/0;会抛出java.lang.ArithmeticException: / by zero
	 * 	    ArithmeticException的父类是RuntimeException;RuntimeException的父类是Exception;所以只有递归调用两次,才能匹配到我指定的Exception.class
	 *
	 * 	    但是,如果我指定的是IoException.class,那永远也匹配不上,因为IOException和ArithmeticException都继承了RuntimeException,是并行的关系,在最后
	 * 	    递归调用到父类Throwable的时候,就会返回-1(即使这里返回了-1,最后事务还是会回滚,为什么?因为在方法之后会判断,如果程序员指定的异常和当前业务代码抛出的异常不相似,那就会判断业务代码抛出的异常是否是运行时异常或者error)
	 *
	 * @param exceptionClass:当前业务代码抛出的异常/或者是抛出异常对应的父类
	 * @param depth:相似度/或者说是深度
	 * @return
	 */
	private int getDepth(Class<?> exceptionClass, int depth) {
		if (exceptionClass.getName().contains(this.exceptionName)) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass == Throwable.class) {
			return -1;
		}
		return getDepth(exceptionClass.getSuperclass(), depth + 1);
	}

所以,在业务代码抛出异常的时候,

  1. 会判断异常的类型和程序员指定的异常类型,会找到匹配度最为相似的异常,最后再判断最相似的异常 是否是noRollbackException配置的异常类,如果是,就无需回滚,提交事务即可
  2. 如果没有匹配上,就判断抛出的异常是否是Error或者RuntimeException,如果是,也会回滚

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/108695188