spring事务失效场景三:内部方法调用

这篇笔记主要记录spring事务失效场景三:在同一个类中,用非事务方法调用事务方法
首先,这种场景,失效的原因是,在非事务方法中,调用同一个类的事务方法,和动态代理没有关系,事务拦截器无法拦截到,就是一个this方法调用。所以,在实际业务开发过程中,一定要避免这种场景

应用

应用其实简单,就是这样的,在非事务方法中,调用了同类中的一个事务方法

spring事务失效场景三:内部方法调用_第1张图片

下面这里是debug的结果

spring事务失效场景三:内部方法调用_第2张图片

可以看到,在进行调用的时候,完全没有被事务拦截器所拦截到,是因为非事务方法testNoTrans(),所以,这里就是一个普通的方法的调用,导致这样的原因是:在cglibAopProxy在拦截到目标方法之后,会根据method,判断当前method所要经过的通知方法有哪些,但是非事务方法,所需要执行的通知方法是空,所以,就直接进行了目标方法的调用,也就是说
1.如果在A.class类中的某些方法,添加了事务注解,那spring是会为该类生成代理对象的
2.在调用该类中的非事务方法的时候,也是会被代理对象所拦截,只是说,如果当前方法所需要执行的通知方法是空,就直接去调用目标方法了
3.如果调用的方法是事务方法,则会被事务拦截器所拦截到,去进行事务的处理

不生效的原理

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
@Override
@Nullable
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 {
		/**
		 * 这里和exposeProxy这个属性设置有关系,如果在@EnableTransactionManagemenr
		 * 注解中设置了exposeProxy属性为true,这里就会设置到threadLocal中一个proxy对象
		 * 在业务代码中,就可以通过AopContext.getCurrentProxy()获取到一个代理对象
		 */
		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);
		/**
		 * 获取目标方法执行时,需要调用的通知方法,然后将对应的advice转换为拦截器放到chain中
		 * 如果chain为空,就直接调用目标方法,否则就通过aop的形式,调用所有的通知方法
		 */
		List 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);
		}
	}
}
 
  

这里的源码,可以看到,在根据方法获取到所有要执行的通知方法之后,会进行一层判断,如果要执行的通知方法为空,就直接调用目标方法

所以,getInterceptorsAndDynamicInterceptionAdvice是核心方法,会根据当前method,去判断当前method所需要经过的通知方法,前面也说过,动态代理对象是针对类的,只要一个类中有添加事务注解,那就会为其生成代理对象,但是在实际调用的时候,会根据method,进行一层判断,只有加了事务注解的方法,才会被拦截器拦截到
如果在非事务方法中,调用事务方法,其实就是一个普通方法的调用,并不会被拦截器拦截到

你可能感兴趣的:(spring源码,java)