美文网首页
从Spring AOP的原理理解@Transactional失效

从Spring AOP的原理理解@Transactional失效

作者: 進撃的Friday | 来源:发表于2019-06-03 21:01 被阅读0次

    转自:https://blog.csdn.net/canot/article/details/80855439,如有侵权,请联系我删除,谢谢

    在正确配置了Spring事务管理后,或许在某些场景下,你可以写出如下代码:

    class T {
    
        public int createFirst(){
           //dosometing....
           try {
               this.createSecond();
           }catch (Exception e){
              throw e;
           }
           //dosometing....
           return 0;
        }
    
    
        @Transactional
        public int createSecond(){
           //dosometing with db....
    
        }
    }
    

    然后在外部通过Spring容器获取T的实例t,当你调用t.createFirst()方法的时候,你会发现添加了@Transactional注解的createSecond()方法并没有运行在事务中。
    为了了解这个问题的原理,首先先需要了解Spring 是如何处理注解的(@Transaction 或 @Async)。

    AOP
    面向切面编程,通俗一点将即是在要执行的方法前或后执行一段代码。类使用Spring MVC中的过滤器,在请求前后执行额外的逻辑,并且该逻辑对实际的方法是透明的。

    AOP的实现原理

    1. 编译时织入:在代码编译时,把切面代码融合进来,生成完整功能的Java字节码,这就需要特殊的Java编译器了,AspectJ属于这一类
    2. 类加载时织入:在Java字节码加载时,把切面的字节码融合进来,这就需要特殊的类加载器,AspectJ和AspectWerkz实现了类加载时织入
    3. 运行时织入:在运行时,通过动态代理的方式,调用切面代码增强业务功能,Spring采用的正是这种方式。动态代理会有性能上的开销,但是好处就是不需要神马特殊的编译器和类加载器啦,按照写普通Java程序的方式来就行了!

    Spring AOP 属于运行时织入,即使用代理模式。或许你看到Spring AOP依赖了AspectJ包会以为Spring AOP是AspectJ来实现的。但其他并不是,Spring AOP只是使用了和AspectJ一样的注解但并没有使用 AspectJ 的编译器或者织入器(Weaver),底层AOP实现与AspectJ是不同的。Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。

    Spring AOP的代理实现

    1.JDK动态代理:JDK动态代理技术。通过需要代理的目标类的getClass().getInterfaces()方法获取到接口信息(这里实际上是使用了Java反射技术。getClass()和getInterfaces()函数都在Class类中,Class对象描述的是一个正在运行期间的Java对象的类和接口信息),通过读取这些代理接口信息生成一个实现了代理接口的动态代理Class(动态生成代理类的字节码),然后通过反射机制获得动态代理类的构造函数,并利用该构造函数生成该Class的实例对象(InvokeHandler作为构造函数的入参传递进去),在调用具体方法前调用InvokeHandler来处理。
    2.CGLib动态代理:字节码技术。利用asm开源包,把代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。采用非常底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

    在我们实际使用Spring AOP时,它包括基于XML配置的AOP和基于@AspcetJ注解的AOP,这两种方法虽然在配置切面时的表现方式不同,但底层都是使用动态代理技术(JDK代理或CGLib代理)。

    现在回头来看本文开头的代码
    createFirst()方法里面直接调用createSecond(……)方法,这里还隐含一个关键字,那就是this,实际上这里调用是这样的:this.createSecond(),this是当前对象。当前对象是T,问题就出在这里,因为要想用事务执行createSecond(……),必须用代理对象执行,因为代理对象会拦截到@Transactional来执行相关的增强,但是此时却直接用T对象调用,绕过了代理对象增强的部分,也就是说代理增强部分失效,@Transactional注解失效。

    如果在外部通过Spring容器获取T实例,然后直接调用createSecond方法,此时createSecond()是被代理的,在代理对象中判断到该方法有@Transactional,则会执行该注解需要的前置增强后(开启事务),然后通过invoke,用实际T对象来调用addOrder()方法执行业务逻辑,然后执行后置增强(回滚or提交)。

    特殊场景的解决办法
    没有用代理对象执行createSecond(……),被T对象抢占了先机。那么解决就是要让代理对象来执行createSecond(……)。

    T t = (T) AopContext.currentProxy(); 
    //获取代理对象
    t.createSecond(); 
    //通过代理对象调用createSecond
    
    //需要在@EnableAspectJAutoProxy添加属性值。
    //@EnableAspectJAutoProxy(exposeProxy = true)
    

    相关文章

      网友评论

          本文标题:从Spring AOP的原理理解@Transactional失效

          本文链接:https://www.haomeiwen.com/subject/cvvhxctx.html