架构师之路(十三)之探讨一下Spring框架中@Transactional注解失效引发的血案及背后机理深究

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,非常感谢 ^_^

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章