当前位置: 首页> 健康> 美食 > 建站公司联系电话_潍坊专业做薪酬绩效_技师培训_seo网站内容优化

建站公司联系电话_潍坊专业做薪酬绩效_技师培训_seo网站内容优化

时间:2025/7/12 2:41:09来源:https://blog.csdn.net/weixin_43281875/article/details/144434867 浏览次数:0次
建站公司联系电话_潍坊专业做薪酬绩效_技师培训_seo网站内容优化

#为什么使用this调用@Transaction注解的事务方法失效?

@Transaction注解定义事务方法时是基于Spring的AOP特性,@Transaction底层使用的是Spring框架的AOP。通过AOP才能在实际方法前后增加其他逻辑,比如事务的启动及回滚。

当一个类UserService使用AOP,即面向切面编程时,Spring会创建UserService的代理对象来拦截对原始对象的调用,这样才能对目标对象的方法进行增强,因此Spring启动时会生成代理对象的bean并放入容器中。后续自动注入UserService类的对象时,获取的也是UserService的代理对象。但当使用new来创建UserService的对象获取的还是原始对象,因为它没有经过spring的管理。

当类UserService中有任何一个方法使用了切面时(如使用了@Transaction),Spring就会创建UserService的代理对象:

  • 打印userService.getClass()的结果(UserService类中方法save使用了@Transaction),结果如下:
> com.sun.test.userService$$EnhancerBySpringCGLIB$$37dac4e3
  • 清除@Transaction后打印结果为:
>  com.sun.test.userService

可见userService的类为CGLIB代理,此外Spring还支持JDK动态代理:

  • JDK动态代理:用于接口,创建的代理对象实现了目标类的接口,只能调用接口中的方法,类是proxy
  • CGLIB代理:用于没有实现接口的类,通过子类化(继承)来创建代理对象,类是EnhancerBySpringCGLIB

Spring默认使用JDK动态代理,但是如果目标类没有实现接口,就会切换到CGLIB代理,上面的userService没有接口,所以代理类是CGLIB。

之所以通过this调用事务方法失效,是因为this调用时并未使用代理对象而是使用原始对象(直接在同一个实例中调用另一个方法,spring无法拦截这个调用),原始对象仅仅使用了@Transaction注解,并未主动启用事务语句或进行回滚处理,而注解不通过代理无法生效,因此事务无法正常生效。只有使用代理对象调用事务方法时,事务管理才会生效,因为spring已经处理好相关逻辑。

但是,spring不是已经注入了代理对象吗,怎么又会出现原始对象呢?

代理对象和原始对象:

  • 代理对象:如果某个类UserService需要使用AOP特性时,Spring就会创建它的代理对象来包装原对象,后续通过@Autowired注入的就是这个代理对象。代理对象负责处理切面逻辑(事务、日志),并在特定点调用原始对象的方法。使用userService.method()就会走spring的代理。
  • 原始对象:没有经过代理的对象,即未使用AOP,那么注入时直接返回原始对象。在实例中使用this调用方法时,也是调用原始实例中的方法。

当bean被创建时,spring会维护一个原始对象和一个代理对象(如果类存在AOP),代理对象是对原始对象的包装,当实例内部使用this调用时会作用于原始对象,而通过对象.方法的方式会经过代理,这就是this调用事务方法失效的原因,因为没有通过spring代理。

验证:UserService有两个方法saveaddsave方法调用add方法,只有add方法添加了@Transaction,controller中注入UserService实例,然后在userService.save之前打印userService的class名字,在save方法中打印this的class名字,两次输出如下:

>com.sun.test.userService$$EnhancerBySpringCGLIB$$37dac4e3
>com.sun.test.userService

与上述描述一致。

更简单一点的理解,当使用代理对象调用非事务方法时,该方法并不需要任何切面操作,那么代理对象会在代理方法中直接调用原始对象的目标方法,那么在这个目标方法中使用this调用时,使用的自然就是原始对象,所以即使用this调用使用注解的事务方法也没用。

而若调用事务方法,存在切面操作,会在调用原始对象目标方法之前之后设定事务启动和出错回滚,这样即使目标方法内通过this调用了其他方法,只要出错就会一起全部回滚,因为都在最外层的事务处理当中,看起来好像是this调用的事务方法奏效,但其实是代理对象调用的事务方法奏效(对应下方解决方案3)。

解决方案:

本质上来说就是得通过代理对象.方法来实现事务方法的正常调用:

  1. 可以将事务方法移出到另一个service中,然后通过新的service调用事务方法
  2. 使用@Autowired注入本身,比如在UserService中注入userService,然后通过userService.调用事务方法
  3. @Transaction往上移一级,使得代理对象能直接调用事务方法
关键字:建站公司联系电话_潍坊专业做薪酬绩效_技师培训_seo网站内容优化

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: