Java中嵌套调用方法时注解失效的原因

以下内容首发于我的个人博客网站:
http://riun.xyz


参考链接: https://www.cnblogs.com/moxiaotao/p/9776964.html

同一个类中,在一个没有注解的方法A中调用另外一个有注解(比如@Async, @Transational)的方法B时,是不会触发B方法的注解的。

比如下面的例子:

有注解标注的方法addUser();没有注解标注的方法selUser()。在selUser()中调用addUser()则不会触发@Transactional注解。

@Service
public class UserService {
    @Resource
    private TestMapper testMapper;

    @Transactional
    public void addUser() {
        Test test = new Test();
        test.setName("888");
        test.setAge(888);
        testMapper.insertSelective(test);
    }

    public Test selUser() {
        Test test = testMapper.selectByPrimaryKey(1);
        addUser();//测试@Transactional是否起作用
        System.out.println(test);
        return test;
    }
}

我们写一个简单的SpringBoot项目将这个service嵌进去。将日志级别设置为debug,这样能详细的看到是否执行了事务。

调用addUser()方法时,创建了一个事务去执行:

2020-05-07 14:58:01.563 DEBUG 101548 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [com.example.demo.service.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

在这里插入图片描述

而调用selUser()方法时,并没有创建事务执行:

在这里插入图片描述

这验证了上面的结论。那这又是为什么呢?

Spring在扫描bean时会扫描方法上的注解,当添加了@Async, @Transational等这些注解时,spring会为这个bean自动生成一个子类【代理类,proxy,关于代理的文章点这里】,此子类继承原来的类,就是原来类的代理类。【比如例子中向addUser()添加注解@Transational时,spring扫描到时会生成一个继承于UserService的代理类UserServiceproxy】,代理类中为有注解的方法插入一个 startTransaction ()方法。

伪代码:

@Service
public class UserServiceproxy$A {
    UserService userService = new UserService();

    public void addUser() {
        startTransaction();
        userService.addUser();
    }
    /*
    只有直接调用UserServiceproxy$A.addUser()时,才会调用到startTransaction();
    */
    
    public Test selUser() {
        userService.selUser();
        /*
        内部调用 userService.addUser()
        而不是 UserServiceproxy$A.addUser()
        */
	}
}

此时,当有注解的方法addUser()被调用时,实际上是调用代理类UserServiceproxy$A的addUser(),所以会执行到startTransaction(); 就能够开启事务。而调用没有注解的方法selUser()时,内部就是直接调用的自身的selUser(),自身的selUser()内部调用的是自身的addUser(),所以并不会开启事务。

那么如何解决这个问题呢?有两种方法:

  • 1、把这两个方法分开写到不同的类中(只需写在不同类中,selUser()方法不用添加注解);

  • 2、把注解加到类名上面(这样该类下的所有方法都拥有此注解的效果);

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