Either pull the method up to an interface or switch to CGLIB proxies by

问题背景

公司使用的是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,后者有个方法加了TransactionalEventListenerAsync注解,但接口中无此方法,只在实现类中存在。
方法如下:
在这里插入图片描述
项目启动本来一直没问题的,结果某次不小心去掉了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.设置@EnableAsyncproxyTargetClass为true,这样即使去掉了atomikos依赖,由于最终生成cglib代理,所以也能获取到TransactionalEventListener注解的方法
2.不设置@EnableAsyncproxyTargetClass属性,直接将实现类的方法也定义在接口中即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值