使用@Async时出现Cannot find current proxy: Set ‘exposeProxy‘ property on

问题场景

导致问题的方法调用链如下

- 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) {
	
	}

参考:从@Async案例找到Spring框架的bug

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值