一、配置
在Springboot中,开启事务特别简单,如下
1.在启动类上或者配置类上增加@EnableTransactionManagement
2.在需要开启事务的方法上增加@Transactional
二、问题
因为本人有个特别不好的习惯,经常把@Transactional注解使用在interface的方法上,最近遇到一个问题,事务不回滚。但在排查问题时debug发现,开启事务的类是以CGLIB方式实现的代理对象,但印象中,如果不配置proxyTargetClass的话,默认应该是false,JDK的动态代理,于是找了一下原因,特此记录一下过程
三、查问题
1.查看@Transactional源码,并没有代理相关配置
2.查看@EnableTransactionManagement,存在代理相关配置,如下
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
* opposed to standard Java interface-based proxies ({@code false}). The default is
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
* {@link AdviceMode#PROXY}</strong>.
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Transactional}. For example, other beans marked with Spring's
* {@code @Async} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*/
boolean proxyTargetClass() default false;
默认值是false,是JDK的动态代理,与记忆符合,但与实际情况不符。
3.因为Spring的声明事务是基于AOP的,所以全文搜索AOP相关字样发现(此方法比较笨),Springboot引入的一个包spring-boot-autoconfigure,在此包下有AOP的配置,并且有proxy-target-class的配置,默认配置为true,并且还有个参数spring.aop.auto的配置,默认为true
//出自spring-boot-autoconfigure包下spring-configuration-metadata.json文件
{
"defaultValue": true,
"name": "spring.aop.auto",
"description": "Add @EnableAspectJAutoProxy.",
"type": "java.lang.Boolean"
},
{
"defaultValue": true,
"name": "spring.aop.proxy-target-class",
"description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).",
"type": "java.lang.Boolean"
},
4.进行验证
application.yaml配置spring.aop.auto | application.yaml配置spring.aop.proxy-target-class | 注解@EnableTransactionManagement 的proxyTargetClass | 代理技术 | 备注 |
true | false | false | JDK动态代理 |
|
true | true | false | CGLIB | 默认值 |
true | false | true | CGLIB |
|
true | true | true | CGLIB |
|
false | false | false | JDK动态代理 |
|
false | true | false | JDK动态代理 |
|
false | false | true | CGLIB |
|
false | true | true | CGLIB |
|
四、总结
当springboot开启spring.aop.auto设置为true时(类似于自动增加了@EnableAspectJAutoProxy注解),则注解配置与配置文件配置有一个配置CGLIB(true),则代理使用CGLIB实现代理,否则使用JDK动态代理
当springboot开启spring.aop.auto设置为false时,代理方式仅仅受注解配置影响。
最后本人修改了注解位置,修改到了具体实现类的方法上。
五、其他说明
基于CGLIB的代理与基于JDK的动态代理实现的声明式事务的区别:
- CGLIB基于继承实现,JDK动态代理基于实现接口实现
- CGLIB的代理类需要事务注解@Transactional标注在类上(或方法)(此处还存在一些疑问,详见https://www.oschina.net/question/3221069_2310769);而JDK动态代理类事务注解@Transactional可以标注在接口上(或方法),也可以标注在实现类上(或方法)