问题场景
导致问题的方法调用链如下
- controller调用接口A的方法B
首先,在controller类中注入了AlarmRuleCheckService接口(其对应的实现类为AlarmRuleCheckServiceImpl ),假设它为
接口A
;
其次,在controller类某方法中,调用接口A
的timerCheckRuleIncBdpOpsAndFalcon方法,假设它为方法B
核心代码如下
public void alarmRuleCheck() {
......
//调用接口A的方法B
alarmRuleCheckService.timerCheckRuleIncBdpOpsAndFalcon();
......
}
- 方法B调用接口A的方法C
方法B
,也就是上述的timerCheckRuleIncBdpOpsAndFalcon方法,会做以下事情
首先,获取
接口A
的代理对象,类型为AlarmRuleCheckServiceImpl ;
其次,通过代理对象调用接口A
的另一个方法timerCheckFalconRuleExistOps,假设它为方法C
核心代码如下
@Override
public void timerCheckRuleIncBdpOpsAndFalcon() {
......
//获取A类的代理对象
AlarmRuleCheckServiceImpl a =(AlarmRuleCheckServiceImpl) AopContext.currentProxy();
//调用接口A的方法C
a.timerCheckFalconRuleExistOps(notice);
......
}
方法B
中使用AopContext.currentProxy()
获取A类的代理对象。
- 方法C加@Async注解
方法C
,也就是上述的timerCheckFalconRuleExistOps方法,加了@Async注解
@Async
public void timerCheckFalconRuleExistOps(Notice notice) {
}
结果B方法
在执行到AopContext.currentProxy()
这一行报错了:
Cannot find current proxy: Set ‘exposeProxy’ property on Advised to
‘true’ to make it available, and ensure that AopContext.currentProxy()
is invoked in the same thread as the AOP invocation context.
问题分析
通过观察,发现接口A
中所有的方法都没有加@Transactional等注解,只有方法C
上加了@Async注解。
在spring源码中,AsyncAnnotationBeanPostProcessor
类会对加了@Async注解的类生成代理对象,但是该类却没有设置exposeProxy属性的逻辑,源码如下:
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
所以,问题的根本原因是:
虽然通过
AsyncAnnotationBeanPostProcessor
生成了接口A
的代理对象,但是由于它的exposeProxy属性不为true,所以通过AopContext.currentProxy()
是获取不到代理对象的。
解决方案
目前项目启动类上已经设置了@EnableAspectJAutoProxy(exposeProxy = true)
,所以,我们可以给方法C
补上@Transactional注解,这样spring中的处理流程就变成下述逻辑:
首先,
接口A
会被交给spring内置的AbstractAutoProxyCreator
处理,以把相应的代理对象生成好;
然后,当AsyncAnnotationBeanPostProcessor
执行的时候,由于已经有代理对象了,于是它会沿用这个代理,只需要把切面添加进去即可。
最终方法C
如下
@Async
//补上事务注解
@Transactional(readOnly = true)
public void timerCheckFalconRuleExistOps(Notice notice) {
}