Spring事务处理机制以及错误使用TransactionSynchronization的afterCompletion方法引起的问题

前言我们都知道spring有声明式事务和编程式事务,声明式只需要提供@Transactional的注解,然后事务的开启和提交/回滚、资源的清理就都由spring来管控,我们只需要关注业务代码即可;而编程式事务则需要使用spring提供的模板,如TransactionTemplate,或者直接使用底层的PlatformTransactionManager。声明式事务的最大优点就是对代码的侵入性...
摘要由CSDN通过智能技术生成

前言

我们都知道spring有声明式事务和编程式事务,声明式只需要提供@Transactional的注解,然后事务的开启和提交/回滚、资源的清理就都由spring来管控,我们只需要关注业务代码即可;而编程式事务则需要使用spring提供的模板,如TransactionTemplate,或者直接使用底层的PlatformTransactionManager。

声明式事务的最大优点就是对代码的侵入性较小,只需要在方法上加@Transactional的注解就可以实现事务;编程式事务的最大优点就是事务的管控粒度较细,在实现某个代码块的事务。

背景

简单介绍完spring的事务机制那就要引入这一次碰到的问题了,我相信大多数人应该和我一样,只要怎么使用,比如加个注解啥的,但是底层原理不清楚。好一点的知道AOP动态代理,再好一点就是知道事务的传播机制(一般也就用用默认的REQUIRED)。真正底层的事务处理的源码很多人应该是没有看过的,当然我也是没有滴~~ 但是这一次碰到的问题让我不得不去看源码了。

这段时间一直在做代码的重构,既然是重新写的代码,当然想写得漂亮一点,不然是要被后人戳脊梁骨的~~ 结果所有代码都写完,都提测了,在测试环境却报一个诡异的异常

java.sql.SQLException: PooledConnection has already been closed

而且这不是必现的,而一旦出现,那任何涉及数据库连接的接口都有可能报这个错。从字面意思看是用到的数据库连接被关闭了,但是理解不能啊,这种底层的事情不都是spring帮忙做好了么。建议测试重启机器,心中期待不会再现

结果依然出现,而且频率还不低,都阻塞测试了。。那就只好操起久违的调试源码的大刀,硬着头皮上了。

过程

本次源码使用的是spring版本是 4.2.4.RELEASE,事务管理器则是参照项目使用的DataSourceTransactionManager。

入口
  1. 首先是事务的入口,spring用的是动态代理,如果某个方法被标注了@Transactional,则会由TransactionInterceptor拦截,在原始方法的前后增加一些额外的处理。可以看到,调用的是TransactionInterceptor的invoke方法,而内部又调用了invokeWithinTransaction方法,但其实这个并不一定会创建事务(事务传播机制里有几种情况是不需要或者不支持事务的)。
public Object invoke(final MethodInvocation invocation) throws Throwable {
	// Work out the target class: may be {@code null}.
	// The TransactionAttributeSource should be passed the target class
	// as well as the method, which may be from an interface.
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
		@Override
		public Object proceedWithInvocation() throws Throwable {
			return invocation.proceed();
		}
	});
}
  1. TransactionInterceptor继承了TransactionAspectSupport这个抽象类,invokeWithinTransaction这个方法是在父类中的。方法里的英文注释是源码中的,中文注释是我加上去的。CallbackPreferringPlatformTransactionManager是实现了PlatformTransactionManager接口,如果使用的事务管理器是CallbackPreferringPlatformTransactionManager的实现,则会将事务的控制交由这个类的execute方法,这里先省略。我们来看一般情况(很多应该用的是DataSourceTransactionManager吧),总的来说可以将这个方法分为几部分:
    在这里插入图片描述
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);

	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		// 如果当前方法需要事务则会创建事务(@Transactional不代表一定创建事务,可以看spring的事务传播机制)
		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 {
		    // 只做一件事,就是把事务的上下文信息改回本事务开始之前的上下文
		    // 因为有可能本事务是被包裹在其他事务中的,可以看spring的事务传播机制
			cleanupTransactionInfo(txInfo);
		}
		// 事务执行成功后将事务的状态信息提交
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}

	else {
		// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallb
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值