springboot中事务什么时候创建代理对象以及在本类中直接调用事务失效的原因

大家都知道,假如在controller里直接调用service的A方法,如果A方法上有@Transactional注解,这个时候这个事务的注解是生效的。但是如果A方法上没有@Transactional注解,然后A方法中又去调用B方法,而B方法有@Transactional注解,这个时候,这个事务的注解是不生效的。

为什么会这样呢?网上很多人都说,这是因为,如果A方法上有@Transactional注解,此时的service对象就是一个代理类对象,会在代理类中开启事务。但是如果A方法上没有注解,此时的service对象就是一个原生对象,即使你在A方法内部调用其它有注解的方法了,因为没有创建代理对象,所以事务注解也不会生效。

那么我的疑问就来了,我的疑问有两个:
1. 都说service是单例的,那为什么不同的调用方式,用的还不是同一个对象呢?
2. 如果我的controller中有两个方法,一个直接调用有@Transactional注解的方法,一个直接调用没有@Transactional注解的方法,那么在系统启动时,注入到controller中的service对象,到底是什么对象?是代理对象?还是原生对象?

基于这两个疑问,我做了一个测试:

如图一和图二所示:我首先在service中建了一个没有注解的test方法,然后在controller中调用这个方法,并打印service对象的class,看看到底是代理类的class还是原生类的class。图三为执行的结果,可以看到,测试没有生成代理类的class,还是原生的class。

 

 

 


然后,我又在service中建了一个test1方法,如图四所示。这个test2方法上面是有@Transactional注解的,并在test中修改了数据库的数据,并抛出异常,以检测事务是否生效。然后在test方法中调用test1方法。controller还是不变,还是直接调用test方法。

 


如图五所示的执行结果看,此时虽然在controller中直接调用的test方法有@Transactional注解,但是此时还是生成了service的代理类对象。但是还有很重要的一点,此时虽然生成了代理类对象,但是事务并没有生效。

 


此时,经过这两次的验证结果,我已经完全明白了这其中的道理:
1. controller调用service中的方法时,到底是用的是service原生对象还是代理对象,它并不取决于你是否直接调用了带有@Transactional的方法。而是在创建service对象时,它会扫描service的类中是否有带@Transactional的方法,如果有,就会创建对象,如果没有,就会创建原生对象。这完全取决于service自己本身的实现,跟你外部如何去调用,一点关系都没有。

2. 我们在做第二次测试的时候,为什么明明已经生成了代理类对象,但是事务还是没有生效呢?这是因为,当controller直接调用service的test方法时,它是通过代理类调用的,代理类发现test方法上没有注解,自然不会开启事务。后面,在test方法中调用test2方法时,虽然test2方法上有事务了,但是此时的调用属于service内部调用,根本就没有通过代理类去调用。所以自然也不会开启事务。

由此可以看出,网上很多人的说法,都是有歧义的,还是要经过自己亲自动手实验,才能得出真知。正所谓:实践出真知。

原作者链接:https://www.toutiao.com/w/1784400867883008/?app=news_article&timestamp=1701774504&use_new_style=1&share_token=03E13DAD-94D9-4EA1-8423-A30124A8AEDE&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1&source=m_redirect&wid=1701776537788

posted @ 2023-12-05 19:52  小陈子博客  阅读(177)  评论(0编辑  收藏  举报