Spring @Transactional 内部调用的失效问题

须知

@Transactional 底层使用TransactionInterceptor类在方法执行前后开启事务和关闭事务

spring的动态代理,分为两种:jdk自身代理和cglib代理方式

参考:https://blog.csdn.net/a837199685/article/details/68930987

objenesis参考:http://objenesis.org/index.html  主要用于不用构造方法初始化实例

场景

case1

接口和实现类,test1中不加@Transactional,test2加@Transactional,外部调用test1()方法

结果:test2方法失败时,事务并不会回滚

//接口
public interface CeshiService {
    void test1();
}


//test1方法上没有Transactional注解,test2方法上有Transactional注解
@Service
public class CeshiServiceImpl implements CeshiService{

    public void test1(){
        test2();
    }

    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        Db.update("INSERT INTO \"public\".\"ceshi\"(\"id\", \"name\") VALUES (2, '2');");
        throw new Exception("异常");
    }
}

分析:

debug发下,CeshiServiceImpl中的this指向的是CeshiServiceImpl@11343 实例,并不是$.Proxy代理对象,那么当也就决定了内部调用test2时,不会走动态代理的链路,那么事务也就不会生效

case2

普通类,test1中不加@Transactional,test2加@Transactional,外部调用test1()方法

结果:test2方法失败时,事务并不会回滚

//test1方法上没有Transactional注解,test2方法上有Transactional注解
@Service
public class CeshiService{

    public void test1(){
        test2();
    }

    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        Db.update("INSERT INTO \"public\".\"ceshi\"(\"id\", \"name\") VALUES (2, '2');");
        throw new Exception("异常");
    }
}

分析:

debug发下,CeshiServiceImpl中的this指向的是CeshiServiceImpl@11342 实例,并不是Enhanced代理对象,那么当也就决定了内部调用test2时,不会走动态代理的链路,那么事务也就不会生效

如何生效

  1. 首先开启exposeProxy,然后((CeshiService) AopContext.currentProxy()).test2();去调用test2内部方法

@Transactional 代码分析

TransactionInterceptor类

public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        //主要来此处的invokeWithinTransaction方法,把真正的方法包围了
        return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
}

TransactionAspectSupport中的invokeWithinTransaction方法

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

		// If the transaction attribute is null, the method is non-transactional.
        //获取注解属性
		final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
		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
                //里面执行事务的提交或回滚
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
						new TransactionCallback<Object>() {
							@Override
							public Object doInTransaction(TransactionStatus status) {
								TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
								try {
									return invocation.proceedWithInvocation();
								}
								catch (Throwable ex) {
									if (txAttr.rollbackOn(ex)) {
										// A RuntimeException: will lead to a rollback.
										if (ex instanceof RuntimeException) {
											throw (RuntimeException) ex;
										}
										else {
											throw new ThrowableHolderException(ex);
										}
									}
									else {
										// A normal return value: will lead to a commit.
										return new ThrowableHolder(ex);
									}
								}
								finally {
									cleanupTransactionInfo(txInfo);
								}
							}
						});

				// Check result: It might indicate a Throwable to rethrow.
				if (result instanceof ThrowableHolder) {
					throw ((ThrowableHolder) result).getThrowable();
				}
				else {
					return result;
				}
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
		}
	}

 

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.hasTransaction()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
            // 注解上指定的回滚异常类型是否匹配
			if (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 ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
				catch (Error err) {
					logger.error("Application exception overridden by rollback error", ex);
					throw err;
				}
			}
			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 ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
				catch (Error err) {
					logger.error("Application exception overridden by commit error", ex);
					throw err;
				}
			}
		}
	}

rollbackOn方法,从此可以看到,默认只要当是RuntimeException或Error异常时才会回滚,非RuntimeException的Exception默认不会回滚

	@Override
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

总结

我们发现不论是jdk动态代理还是cglib动态代理,执行类方法时,this对象始终是原始实例,也就决定了,内部调用时,不会触发动态代理里面的方法。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值