Análisis del proceso de ejecución del código fuente de la transacción de Spring

El blog anterior analizó el código subyacente del objeto de proxy dinámico generado por la transacción. El resumen simple es una oración: si se agrega la anotación de transacción @Transactional y el método es público, Spring generará un objeto de proxy para el bean. si es jdk o cglib, depende de si la clase correspondiente a su método de transacción implementa la interfaz.
A continuación, veamos el proceso de ejecución después de que se genera el objeto proxy.

Ejecución del interceptor

org.springframework.transaction.interceptor.TransactionInterceptor

Cuando estaba estudiando, usé el proxy cglib. Como no implementé la interfaz, usé el proxy CGLIB. Cuando se llame al método de destino, se llamará

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

Corté una parte de este método para distinguir entre transacciones declarativas y transacciones programáticas. Aquí solo nos enfocamos en transacciones declarativas

@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;
	}
}

En este método,

  • De acuerdo con el mecanismo de propagación correspondiente a la anotación de la transacción, determine si crear una nueva transacción o ejecutar anidada en la transacción actual, etc .;
  • Luego ejecuta el método de destino
  • Después de ejecutar el método de destino, si no ocurre ninguna excepción, confirme la transacción
  • Si ocurre una excepción, determine si se debe revertir de acuerdo con rollbackFor y noRollbackFor configurados. Si no hay necesidad de revertir, envíe la transacción; de lo contrario, revertir la transacción

Por tanto, nos centramos en dos métodos

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

Código fuente del mecanismo de transmisión

Agregaré el código fuente del mecanismo de transmisión después de terminar

Código fuente de reversión de excepción

Si ocurre una excepción durante la ejecución del método de destino, después de capturarla, ingresará a este método para su procesamiento.

/**
 * 这里是判断是否需要回滚的逻辑
 * 如果在事务注解上指定了回滚的异常类型、或者指定了不回滚的异常类型,就会在这里进行判断
 * 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;
			}
		}
	}
}

Uno de los métodos más importantes aquí es juzgar las excepciones y determinar si las excepciones lanzadas por el código comercial actual cumplen con los requisitos de reversión;

Inserte la descripción de la imagen aquí

Este método aquí es para determinar si la excepción lanzada actualmente se ajusta a la información de reversión de excepciones configurada en la anotación @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);
	}

Entonces veamos el método anterior para juzgar la similitud anormal: int depth = rule.getDepth (ex);

Aquí lo llamo similitud, no sé si es apropiado; aquí está la comparación entre el nombre correspondiente a la excepción A arrojada en el código comercial y el nombre correspondiente a la excepción B especificada por el programador. Si coincide, devuelve la profundidad, si coincide. Si no, llame de forma recursiva, compare la clase principal correspondiente a la excepción A lanzada en el código comercial con el nombre de la excepción B, y llame de forma recursiva a su vez

/**
	 * 这里其实就是用业务代码抛出的异常和程序员在@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);
	}

Por lo tanto, cuando el código comercial arroja una excepción,

  1. Juzgará el tipo de excepción y el tipo de excepción especificado por el programador, encontrará la excepción con el grado de coincidencia más similar y finalmente determinará si la excepción más similar es la clase de excepción configurada por noRollbackException. Si es así, no es necesario retroceder y enviar la transacción.
  2. Si no hay coincidencia, juzgue si la excepción lanzada es Error o RuntimeException, si lo es, también se revertirá

Supongo que te gusta

Origin blog.csdn.net/CPLASF_/article/details/108695188
Recomendado
Clasificación