“
Spring 事务管理分为编程式和声明式两种。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体的逻辑与事务处理解耦。
声明式事务有两种方式,一种是在配置文件(XML)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式
”
好久没写文章了,其实很想写,一直没机会,也没时间,要么有时间了,就要在家带孩子,要么就在出差的路上,一旦出差就更没自己的时间了,因为大家都知道的原因呗,搞IT的,我觉得很少能有自己的时间吧,读者应该都懂得.....
最近一直忙于项目,而且项目确实也很重要,全国人民都在关注,本人在项目中也担负着很关键的责任和使命,至于是啥项目,嘿嘿,恕不概述。反正每天都见不到太阳,回来又很晚,很疲惫,其实有很多内容想表达,奈何精力有限,今天刚好挤出时间,来跟大家聊聊做项目过程中,遇到的坑,这个事务机制就是其中很关键的一个。
废话不多说了,直接跟大家上干货
注解事务原理
我相信但凡有过3、5年开发经验的读者,一定知道@Transactional这个注解,(如果不清楚,建议您也别读我的这篇文章了,因为即使是读了,您也看不懂,您说对吧?),注解很简单,英文单词也很好翻译,在Service层目标方法上,添加这么个玩意,就能实现事务的回滚了,但你真的知道它的使用场景以及使用原理么?真的没犯过错么?
别的不多说了,我先用两张时序图给大家简单说一下@Transactional深入实现原理吧,直接上图:
图画的可能不是特别好,大家就先将就着看了哈
解释一下名词,以下这几个即将登场的词,将会是SpringAop联盟中最重量级的角色,也是事务管理整个逻辑中的中流砥柱:
1) CglibAopProxy:
这玩意是Spring中事务拦截的排头兵,冲锋陷阵,使用Cglib代理实现SpringAop家族的切面拦截,专注于代理类、代理对象的生成,是创建
代理的核心方法
2) AdvisedSupport
这货主要是专注于配置当前代理对象相关信息,扫描Spring容器中的所有Advisiors,并判断是否为PointcutAdvisor类型实例,并将符合条件的Advisiors,通过DefaultAdvisorChainFactory适配成MethodInterceptor,最终生成拦截器代理列表,本身不做代理对象创建的动作,只是扫描配置代理信息,确切的讲就是生成一个装有方法拦截器的list集合
3) ReflectiveMethodInvocation
这货是唯一实现了MethodInvocation的类,专门用于拦截器链中的所有代理类的调用
4) TransactionInterceptor
这货是整个事务控制的核心模块,所有的事务操作都是通过这货来实现,比如事务的准备、事务开启、事务绑定、以及事务的提交、异常回滚,是SpringAop联盟中,切面拦截的最终实现,也就是通过这货实现了代理方法的前后增强,完成了事务处理
5) TransactionAspectSupport
这货是上一个货(TransactionInterceptor)的父类,是对上一个货的深层次封装,核心部件都在这里实现,不再赘述
上图中的TransactionInterceptor更深层次的处理逻辑没有画全,再给大家上一张图,用于描述Spring容器在真正托管事务管理之后,后续是如何实现的事务拦截以及事务提交、回滚动作,废话不多说,直接上图咯:
源码深入解读
CglibAopProxy 核心逻辑
@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(); Object var16; try { if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); Class<?> targetClass = target != null ? target.getClass() : null; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed(); } retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal); var16 = retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } return var16; }
可以看到,在上述的源码中有个很关键的代码CglibAopProxy.CglibMethodInvocation,这段代码的含义是创建一个代理类,代理类的类型为CglibAopProxy,并初始化代理链拦截器列表interceptorsAndDynamicMethodMatchers
ReflectiveMethodInvocation 核心逻辑
@Nullable public Object proceed() throws Throwable { if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return this.invokeJoinpoint(); } else { Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed(); } else { return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this); } } }
可以看到这段代码的核心聚焦在proceed上,该代码段其实只做了一件事,那就是将代理链拦截器列表做了深层次遍历,循环扫描代理链列表,递归调用,直到所有的方法拦截器(MethodInterceptor)都调用结束。
TransactionInterceptor 核心逻辑
@Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable { TransactionAttributeSource tas = this.getTransactionAttributeSource(); TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null; PlatformTransactionManager tm = this.determineTransactionManager(txAttr); String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr); Object result; if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) { TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder(); try { result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> { TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); Object var9; try { Object var8 = invocation.proceedWithInvocation(); return var8; } catch (Throwable var13) { if (txAttr.rollbackOn(var13)) { if (var13 instanceof RuntimeException) { throw (RuntimeException)var13; } throw new TransactionAspectSupport.ThrowableHolderException(var13); } throwableHolder.throwable = var13; var9 = null; } finally { this.cleanupTransactionInfo(txInfo); } return var9; }); if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } else { return result; } } catch (TransactionAspectSupport.ThrowableHolderException var19) { throw var19.getCause(); } catch (TransactionSystemException var20) { if (throwableHolder.throwable != null) { this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable); var20.initApplicationException(throwableHolder.throwable); } throw var20; } catch (Throwable var21) { if (throwableHolder.throwable != null) { this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw var21; } } else { TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification); result = null; try { result = invocation.proceedWithInvocation(); } catch (Throwable var17) { this.completeTransactionAfterThrowing(txInfo, var17); throw var17; } finally { this.cleanupTransactionInfo(txInfo); } this.commitTransactionAfterReturning(txInfo); return result; } }
可以看到,该段代码主要完成了事务的具体操作,也就是事务在Spring容器中从准备开启、绑定至本地线程变量到最终解绑的整个生命周期,详细描述了事务是提交的时机、抛出异常回滚的时机以及最终回收的整体逻辑
TransactionAspectSupport 事务提交核心逻辑
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (this.logger.isTraceEnabled()) { this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
可以看到,最底层的事务提交操作,最终还是调用的DatasourceTransactionManager实现,也就是最终将提交的动作交给数据库去做
TransactionAspectSupport 事务回滚核心逻辑
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (this.logger.isTraceEnabled()) { this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException var6) { this.logger.error("Application exception overridden by rollback exception", ex); var6.initApplicationException(ex); throw var6; } catch (Error | RuntimeException var7) { this.logger.error("Application exception overridden by rollback exception", ex); throw var7; } } else { try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException var4) { this.logger.error("Application exception overridden by commit exception", ex); var4.initApplicationException(ex); throw var4; } catch (Error | RuntimeException var5) { this.logger.error("Application exception overridden by commit exception", ex); throw var5; } } } }
可以看到,最底层的事务回滚操作,最终还是调用的DatasourceTransactionManager实现,也就是最终将回滚的动作交给数据库去做
遇到过的大坑
作者在实际运用过程中,也曾遇到过增加@Transactional注解不生效的情况,通过上文对源码的分析,可以总结出来,使用注解不生效的情况无非有以下几种:
1)@Transactional 注解的方法不是public修饰
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); this.methodProxy = methodProxy; this.publicMethod = Modifier.isPublic(method.getModifiers()); }
因为源码已经告诉你了,在创建代理对象的时候,必须是public方法,上述源码中Modifier.isPublic(method.getModifiers())已经明确了
2)内部方法调用
同一个类中,如果方法入口没有加@Transactional 注解,而被调用的方法加了此注解,这种情况视为无效,如下贴出的代码示例,因为这种情况,Spring容器没法通过Cglib生成代理类,自然也就无法使用TransactionInterceptor拦截了
@Override public int doSth() { doBefore(); log.info("执行某操作......"); doAfter(); return 0; } @Transactional(propagation= Propagation.NOT_SUPPORTED, isolation = Isolation.READ_COMMITTED, rollbackFor = RuntimeException.class) int doBefore() { log.info("执行某操作之前......"); return 0; }
3)虽然加了try catch,但异常没有抛出
这种情况也非常常见,捕获了异常而没有向上抛出,而是内部自己消化处理了,那么虽然使用了动态代理,但由于异常本地被捕获处理,代理方法是感知不到的,也就没法触发事务的回滚机制
4)数据库本身不支持事务操作
这种情况一般不常见,因为事务能否生效关键的还是要看底层数据库是否支持,通过分析源码可以看出来,最终事务操作还是交由数据库实现,比如Mysql数据库,引擎一旦从innodb切换成myIsam,那就从根本上生效了,那就不要谈事务是否生效的问题了
写在文章最后
OK,这篇文章也是百忙之中抽出有限的时间,一点一点堆出来的,在日常的工作中,会遇到各种各样的问题,有些问题可能一眼看出原因,但知其然并一定知其所以然,比如这个@Transactional注解,背后的机理还是一知半解,今天给大家啰嗦了那么多,希望能对大家有所帮助,感谢!
写作不易,您的鼓励是我前进的最大动力,如果能切实帮助到您,无论金额多少,都是一份心意 o ( ̄︶ ̄) o,非常感谢 ^_^