当前位置: 首页 > 后端技术 > Java

Spring事务控制AOP包围切入底层原理

时间:2023-04-01 17:26:04 Java

前面我们学习了Spring事务框架和@EnableTransactionManagement,实现了Spring事务管理,了解了Spring事务管理的底层工作机制。Spring事务管理还有待学习的是事务如何生效,以及事务生效后具体的AOP切入和增强过程。我们先研究一下事务生效后具体的AOP切入和增强过程。基于Cglib的AOP增强是通过拦截器Interceptor实现的。通过前面文章的分析,我们知道Spring事务的拦截器是TransactionInteceptor,我们直接从拦截器的invoke方法入手。TransactionInteceptor#invoke看一下TransactionInterceptor的类结构:AOPAllianceMethodInterceptor,用于使用通用的Spring事务基础设施(PlatformTransactionManager/org.springframework.transaction.ReactiveTransactionManager)进行声明式事务管理。派生自包含与Spring底层事务API集成的TransactionAspectSupport类。TransactionInterceptor只是以正确的顺序调用相关的超类方法,例如invokeWithinTransaction。采用AOP联盟的方法拦截器实现基于Spring事务框架的事务管理功能。继承自集成Spring底层事务管理API的TransactionAspectSupport类。所以通过JavaDoc我们知道TransactionIncepor是Spring事务管理中的一个方法拦截器,它有一个父类叫TransactionAspectSupport。开始分析他的invoke方法。首先获取targetClass,其实就是@Transactional注解的业务类,通过参数调用传入。@Override@NullablepublicObjectinvoke(MethodInvocationinvocation)throwsThrowable{//计算出目标类:可能是{@codenull}。//TransactionAttributeSource应该传递给目标类//以及可能来自接口的方法。ClasstargetClass=(invocation.getThis()!=null?AopUtils.getTargetClass(invocation.getThis()):null);然后调用invokeWithinTransaction方法,这个方法是在TransactionAspectSupport类中实现的,我们后面会分析。//适配TransactionAspectSupport的invokeWithinTransaction...returninvokeWithinTransaction(invocation.getMethod(),targetClass,newCoroutinesInvocationCallback(){@Override@NullablepublicObjectproceedWithInvocation()throwsThrowable{returninvocation.proceed(Object);p}ar(){returninvocation.getThis();}@OverridepublicObject[]getArguments(){returninvocation.getArguments();}});}我们简单看一下invokeWithinTransaction的最后一个参数CoroutinesInvocationCallback(),也就是拦截器的回调参数。当拦截器增强方法时,会使用这个回调参数调用proceedWithInvocation方法来执行被代理对象的原始方法。TransactionAspectSupport#invokeWithinTransaction第一步是获取TransactionAttributeSource,通过TransactinAttributeSource获取TransactionAttribute。TransactionAttribute是一个交易定义对象,TransactionAttributeSource是一个交易定义对象的持有者。我们之前已经分析过这些概念。protectedObjectinvokeWithinTransaction(Methodmethod,@NullableClasstargetClass,finalInvocationCallbackinvocation)throwsThrowable{//如果事务属性为null,则该方法是非事务性的。TransactionAttributeSourcetas=getTransactionAttributeSource();最终tttas!=null?tas.getTransactionAttribute(方法,targetClass):null);然后通过TransactionAttributeSource获取TransactionAttribute,调用getTransactionAttribute方法获取。我们稍后会分析这个过程。然后通过TransactionAttribute调用determineTransactionManager获取TransactionManager:finalTransactionManagertm=determineTransactionManager(txAttr);determianTransactionManager的代码将不会发布。有兴趣的可以自己看看。代码不会太长。大体的逻辑就是获取TransactionManager,并缓存起来,下次直接从缓存中获取。第一次通过以下代码获取:defaultTransactionManager=this.beanFactory.getBean(TransactionManager.class);其实就是从Spring的Ioc容器中获取的。至于将哪个TransactionManager注入到容器中,其实要看应用。应用程序可以通过Configuration方法和注解方法注入。如果SpringBoot项目引入了JDBC,TransactionManager就是JDBCTransactionManager。后面一大段代码是响应式事务,略过。然后将TransactionManger转为PlatformTransactionManager(不是响应式事务,那么一定是PlatformTransactionManager)PlatformTransactionManagerptm=asPlatformTransactionManager(tm);finalStringjoinpointIdentification=methodIdentification(方法,targetClass,txAttr);然后运行到这个分支,因为or条件的后半部分调用了createTransactionIfNecessary,这是事务控制的关键步骤:开启一个事务。我们稍后也会处理这部分if(txAttr==null||!(ptminstanceofCallbackPreferringPlatformTransactionManager)){//使用getTransaction和提交/回滚调用的标准事务划分。TransactionInfotxInfo=createTransactionIfNecessary(ptm,txAttr,joinpointIdentification);之后,我们知道的下一件事就是我们必须回调业务方法。前面我们已经提到了回调方法,这里不再赘述。需要特别注意的是回调后的处理:如果回调后的处理抛出异常,则调用completeTransactionAfterThrowing,然后继续抛出异常。如果回调业务方法正常返回,调用commitTransactionAfterReturning(txInfo)提交事务。对象返回值;try{//这是一个绕过建议:调用链中的下一个拦截器。//这通常会导致调用目标对象。retVal=调用。proceedWithInvocation();}catch(Throwableex){//目标调用异常completeTransactionAfterThrowing(txInfo,ex);扔前;}最后{cleanupTransactionInfo(txInfo);}//省略部分代码commitTransactionAfterReturning(txInfo);返回值;:事务启动过程。回调正常返回后的事务提交处理。异常发生后的回调事务回滚处理。createTransactionIfNecessary这是启动事务的方法。必要时调用事务管理器的getTransaction来启动事务(根据事务传播机制)。代码很简单,我们之前分析过事务管理器的getTransaction。打开事务后,会调用一个方法prepareTransactionInfo,将当前打开的事务信息绑定到当前线程,并存储到当前对象中。具体功能暂未研究,但对我们对整个Spring事务管理机制的理解影响不大,可以忽略。//做一些准备工作,省略代码TransactionStatusstatus=null;如果(txAttr!=null){如果(tm!=null){status=tm.getTransaction(txAttr);}else{if(logger.isDebugEnabled()){logger.debug("跳过事务连接点["+joinpointIdentification+"]因为没有配置事务管理器");}}}返回prepareTransactionInfo(tm,txAttr,joinpointIdentification,status);如果commitTransactionAfterReturning回调业务方法正常,则调用该方法完成事务的提交。这个方法的代码很短,思路很清晰。直接调用事务管理器的commit方法提交事务。事务管理器的commit方法在我们之前关于Spring事务框架底层逻辑的文章中已经详细分析过了。这相当于从应用入口的角度开始分析,非常符合对Spring事务框架底层逻辑的分析。protectedvoidcommitTransactionAfterReturning(@NullableTransactionInfotxInfo){if(txInfo!=null&&txInfo.getTransactionStatus()!=null){if(logger.isTraceEnabled()){logger.trace(“完成交易[”+txInfo.getJoinpointIdentification()+"]");}txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}completeTransactionAfterThrowing回调方法在回滚处理后返回异常,这种情况在我们日常的项目开发过程中经常会遇到在项目开发和测试的情况下,代码运行到这个分支的次数可能比运行到commit分支的次数多。毕竟程序员天生就是写bug的。上来先做个判断。一般开启交易,肯定能跑进去。if(txInfo!=null&&txInfo.getTransactionStatus()!=null){然后再判断如果启用了事务,则transactionAttribute不会为空。关键是需要注意后面的rollbackOn判断。如果判断为Iftrue,则进去做事务回滚处理。如果(txInfo.transactionAttribute!=null&&txInfo.transactionAttribute.rollbackOn(ex)){尝试{txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());DefaultTransactionAttribute的rollbackOn实现很简单,抛出的异常就算判断是运行时异常还是Error。这就相当于找到了一个底层依据,即Spring事务管理机制默认只对RuntimeException或Error进行事务回滚,对其他异常不进行事务回滚。@OverridepublicbooleanrollbackOn(Throwableex){return(exinstanceofRuntimeException||exinstanceofError);但是,我们可以在定义事务的时候指定(通过@Transactional注解)事务回滚的异常,但是这个实现呢呢?还没找到,疑惑。嗯,Spring事务的AOP绕过去了:业务方法调用预先启动事务,方法调用后根据调用结果提交事务的代码层分析,或者回滚事务。希望你能从这里得到一些东西。AbstractFallbackTransactionAttributeSource#getTransactionAttribute确定此方法调用的事务属性。如果没有找到方法属性,则默认为类的事务属性。Params:method–当前调用的方法(永远不会为null)targetClass–targetClass()返回:此方法的TransactionAttribute,如果该方法不是事务性的,则为null获取当前方法的事务属性。如果没有定义方法的Attribute,则默认返回当前类的Attribute。事务属性将被缓存。首先判断缓存中是否有当前类和方法的Attribute数据,如果有则从缓存中获取。如果缓存不存在,调用computeTransactionAttribute获取后,写入缓存并返回。computeTransactionAttribute默认情况下,只有公共方法支持@Transactional注解。如果不是公共方法,则不会处理。//不允许非公共方法,如配置的那样。如果(allowPublicMethodsOnly()&&!Modifier.isPublic(method.getModifiers())){返回null;}调用getMostSpecificMethod,根据方法的注释和JavaDoc,是为了获取标有@Transactional注解的实体方法,因为注解可能标注在接口上,此时getMostSpecificMethod可以找到对应的方法接口实现类。//该方法可能位于接口上,但我们需要目标类的属性。//如果目标类为null,则方法不变。方法specificMethod=AopUtils.getMostSpecificMethod(method,targetClass);接下来,get这个方法的@Transactional注解生成一个Attribute对象//首先尝试的是目标类中的方法。TransactionAttributetxAttr=findTransactionAttribute(specificMethod);如果(txAttr!=null){返回txAttr;}如果没有获取到方法层的注解如果是的话,获取类上的注解。//第二个尝试是目标类上的事务属性。txAttr=findTransactionAttribute(specificMethod.getDeclaringClass());如果(txAttr!=null&&ClassUtils.isUserLevelMethod(method)){returntxAttr;如果没有获取到,并且当前方法是前面处理过程中的接口方法,已经作为实现类的方法处理过,则返回处理原方法。处理逻辑还是先看方法层,再看类层。如果最后没有得到,则返回null。在这种情况下,此方法未使用@Transactinal注释。if(specificMethod!=method){//Fallback就是看原来的方法。txAttr=findTransactionAttribute(方法);如果(txAttr!=null){返回txAttr;}//最后一个回退是原始方法的类。txAttr=findTransactionAttribute(method.getDeclaringClass());如果(txAttr!=null&&ClassUtils.isUserLevelMethod(method)){returntxAttr;具体的解析过程会交给注解解析器。注解解析器上一篇文章简单的提到过,没有做详细的分析,这里就不做分析了。他的目的是解析@Trnsactional注解,获取事务传播机制、回滚条件、事务超时设置等事务定义的相关属性。最终生成TransactionAttribute后返回。所以……Spring事务的介绍,底层实现机制,业务处理时事务录入和校验的底层实现原理等涉及到Spring事务控制的核心流程,代码层面的分析已经基本已完成。希望它能帮助你。上一篇启用Spring事务管理@EnableTransactionManagement(二)下一篇SpringSecurity初始化流程