问题背景
公司使用的是springboot项目,在启动类中排除了事务管理器相关的配置类,如下
@SpringBootApplication(scanBasePackages = "com.sf.*",
exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
同时,还引入了atomikos数据源的maven依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
当前,项目中FalconGrpHostService
接口的实现类为FalconGrpHostServiceImpl
,后者有个方法加了TransactionalEventListener
和Async
注解,但接口中无此方法,只在实现类中存在。
方法如下:
项目启动本来一直没问题的,结果某次不小心去掉了atomikos数据源的依赖,启动时就报如下错
Need to invoke method 'refreshHostGroupAlarmTarget' declared on target
class 'FalconGrpHostServiceImpl', but not found in any interface(s) of
the exposed proxy type. Either pull the method up to an interface or
switch to CGLIB proxies by enforcing proxy-target-class mode in your
configuration.
问题分析
根据对spring源码的理解,猜测原因是FalconGrpHostService
接口中没有方法导致,流程如下
spring实例化bean后,收集有
TransactionalEventListener
注解的方法时,会先判断所在类是否有此方法。
此时,发现所在类为spring生产的jdk代理类,于是在接口中找方法,但实际接口中无此方法,所以报错。
虽然知道了原因,但是有几个疑问让人困惑
首先,为什么之前不报错,而我把atomikos依赖去掉后就报错?是不是之前用的cglib,后来用的jdk?
其次,项目启动类已经通过@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)设置了强制用cglib,即使我把atomikos去掉,为什么还会生产jdk代理?
断点分析
为了解决上述困惑,我借助断点来一步步对源码进行做了分析,最终观察到的结果如下:
- 当引入atomikos依赖时
1.首先实例化controller
2.由于controller注入了该接口,于是实例化FalconGrpHostService
接口
3.遍历BeanPostProcessor
4.AnnotationAwareAspectJAutoProxyCreator
获取到了事务相关的advisor,由于proxyTargetClass
为true,于是产生cglib代理
- 当去掉atomikos依赖时
1.首先实例化controller
2.由于controller注入了该接口,于是实例化FalconGrpHostService
接口
3.遍历BeanPostProcessor
4.AnnotationAwareAspectJAutoProxyCreator
此时未获取到事务的advisor,但是AsyncAnnotationBeanPostProcessor
获取到了(因为方法有加Async
注解),于是会通过如下方法产生代理
在这一步中,AsyncAnnotationBeanPostProcessor
也会使用@EnableAsync
中的proxyTargetClass
属性值来决定生成什么类型的代理,但是目前项目中并没有设置,所以该属性值默认为false
于是,最终产生了jdk代理而不是cglib代理。
根本原因
至此,我们可以回答刚才提到的两个疑问:
为什么之前不报错,而我把atomikos依赖去掉后就报错?是不是之前用的cglib,后来用的jdk?————————是的,后来用的jdk代理
项目启动类已经通过@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)设置了强制用cglib,即使我把atomikos去掉,为什么还会生产jdk代理?————————因为@EnableAsync
中的proxyTargetClass
属性值默认为false
解决方案
如果非要去掉atomikos依赖,那么有以下解决方案
1.设置
@EnableAsync
的proxyTargetClass
为true,这样即使去掉了atomikos依赖,由于最终生成cglib代理,所以也能获取到TransactionalEventListener
注解的方法
2.不设置@EnableAsync
的proxyTargetClass
属性,直接将实现类的方法也定义在接口中即可。