zoukankan      html  css  js  c++  java
  • Transactional 注解与 AOP

    转载 为防止消失 如有侵权请联系 原链接 https://www.dazhuanlan.com/2019/11/30/5de14b06398b8/

    当我们在一个 private 方法上打上 @Transactional 注解,IDEA 会提示 Methods annotated with '@Transactional' must be overridable。比如下面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Service
    public class UserService {

    public void method1(){
    method2();
    }

    @Transactional
    private void method2(){

    }
    }

    有时候我们想让事务的范围尽可能的小,可能就会写出这样的代码,在 method1 先做一些非事务的事情,然后在 method2 中做事务相关的事情。此时,如果我们用 IDEA 的话,应该就会出现上面的提示信息,eclipse 我没用不清楚会不会有这样的其实。为什么被 @Transactional 注解的方法必须是可重写的呢?

    如果我们忽略掉这个信息,你会发现编译运行是没有问题的。在外部调用 UserService.method1() 时,如果 method2 没有发生异常的话,你会发现一切都是正常的。为什么 IDEA 要这样提示呢,真是怪了!

    但是,当 method2 发生异常时,你会惊奇的发现 method2 的事务并没有回滚!

    AOP

    要理解事务为什么没有回滚,我们就要回顾一下 AOP 的知识。在 Spring 中默认是通过 JDK 动态代理的方式来实现 AOP 的,对于打了 @Transactional 注解的类,Spring 动态代理会生成一个代理 bean 和一个真实的目标 bean。

    我们回头看看 method1 并没有打上注解,所以 method1 并不会被事务切面环绕。而 method2 是通过 method1 调用的,隐藏的调用对象是真实的目标 bean,真实的目标 bean 是没有切面逻辑的,切面逻辑都在代理 bean 上。这就是为什么事务没有回滚的原因,如下图所示:

    transactional-aop

    按照这样的理解,只要我们在 method1 方法上打上 @Transactional 注解,事务就能生效了,method2 的注解是多余的。此时,我们应该就能理解 IDEA 的善意提示了。因为 UserService 没有接口,所以只能通过 CGLIB 的方式来实现动态代理,而 CGLIB 是通过继承的方式来进行代理,需要对目标 bean 的方法进行重写,但是 private 修饰的方法是不能重写的,所以就会出现这样的提示。

    但是,在 method1 上打注解,method2 上不打,那不是违背了我们当初让事务范围最小化的出发点了吗?一切又回到了原地。还有没有其他解决方法?

    1. 把 method1 方法搬到 controller 层,嘿嘿,这个方法看起来很鸡贼,但是好像是更加合理的?
    2. 如果代理类和目标类合为一个,有注解的方法有切面环绕,没有注解的方法没有切面环绕,这样是不是就行了?AspectJ 静态代理就是这样的一门技术,在编译成 class 字节码的时候在方法周围织入切面逻辑。

    总结

    不仅仅是 @Transactional 会这样,其他的注解也是同理的,所以我们要深入理解 AOP 的原理,面对这些问题才能游刃有余。

  • 相关阅读:
    笔记-归并排序
    Repeated Substring Pattern
    Assign Cookies
    Number of Boomerangs
    Paint Fence
    Path Sum III
    Valid Word Square
    Sum of Two Integers
    Find All Numbers Disappeared in an Array
    First Unique Character in a String
  • 原文地址:https://www.cnblogs.com/YoungSone/p/12616522.html
Copyright © 2011-2022 走看看