以下内容首发于我的个人博客网站:
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、把注解加到类名上面(这样该类下的所有方法都拥有此注解的效果);