@Async doesn't work well with AopContext
First, let's see a example below:
the demo's link:https://github.com/lixiaolong11000/async
class AsynApplicationTests {
@Test
void contextLoads() {
}
@Test
public void asynExposeProxy() throws InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfigure.class);
AsyncTransactionalTestBean asyncTransactionalTestBean = ctx.getBean(AsyncTransactionalTestBean.class);
AsyncTestBean asyncTestBean = ctx.getBean(AsyncTestBean.class);
assertThat(AopUtils.isAopProxy(asyncTransactionalTestBean)).as("asyncTransactionalTestBean is not a proxy").isTrue();
assertThat(AopUtils.isAopProxy(asyncTestBean)).as("asyncTestBean is not a proxy").isTrue();
asyncTransactionalTestBean.testTransToAsync();
asyncTransactionalTestBean.testAsyncToAsync();
asyncTestBean.testAsyncToAsyncTOAsync();
ctx.close();
}
@Service
public static class AsyncTransactionalTestBean {
@Transactional
public Collection<?> testTransToAsync() {
System.out.println("testTransToAsync " + Thread.currentThread().getName());
((AsyncTransactionalTestBean) AopContext.currentProxy()).testAsyncToAsync();
return null;
}
@Async
public void testAsyncToAsync() {
System.out.println("testAsyncToAsync " + Thread.currentThread().getName());
((AsyncTransactionalTestBean) AopContext.currentProxy()).testAsync();
}
@Async
public void testAsync() {
System.out.println("testAsync " + Thread.currentThread().getName());
}
}
@Service
public static class AsyncTestBean {
@Async
public Collection<?> testAsyncToAsyncTOAsync() {
System.out.println("testAsyncToAsyncTOAsync " + Thread.currentThread().getName());
((AsyncTransactionalTestBean) AopContext.currentProxy()).testAsyncToAsync();
return null;
}
@Async
public void testAsyncToAsync() {
System.out.println("testAsyncToAsync " + Thread.currentThread().getName());
((AsyncTransactionalTestBean) AopContext.currentProxy()).testAsync();
}
@Async
public void testAsync() {
System.out.println("testAsync " + Thread.currentThread().getName());
}
}
@EnableAsync
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration
@EnableTransactionManagement
static class AppConfigure {
@Bean
public AsyncTransactionalTestBean asyncTransactionalTestBean() {
return new AsyncTransactionalTestBean();
}
@Bean
public AsyncTestBean asyncTestBean() {
return new AsyncTestBean();
}
@Bean
public PlatformTransactionManager txManager() {
return new MockTransactionManager();
}
}
}
run AsynApplicationTests.asynExposeProxy() we can find the exception:
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at com.example.asyn.AsynApplicationTests$AsyncTransactionalTestBean.testAsyncToAsync(AsynApplicationTests.java:64) ~[test/:na]
at com.example.asyn.AsynApplicationTests$AsyncTransactionalTestBean$$FastClassBySpringCGLIB$$adeba3a5.invoke(<generated>) ~[test/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) ~[spring-aop-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_181]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_181]
Second, I find the reason is : @Async doesn't work well with AopContext. because:
- @EnableAspectJAutoProxy(exposeProxy = true) can make AopContext work in current Thread,not work in another Thread. so the test above asyncTransactionalTestBean.testTransToAsync() result in Exception
- the test above asyncTransactionalTestBean.testAsyncToAsync() result in Excepton because AsyncAnnotationBeanPostProcessor、AsyncExecutionInterceptor、AsyncAnnotationAdvisor not surport AopContext.
Third, summary
- When Aop(cglib,jdkproxy) use AopContext,@Async doesn't work well
- @Aysnc is also a Aop, but it not suport AopContext
Final, I want contribute to spring to enhance it, thanks
Comment From: jhoeller
On review, we see AopContext
as a special-purpose feature for circular call arrangements in certain AOP scenarios, so really not meant to be broadly applied. In particular for asynchronous execution, we do not intend to support this at all, and we might even deprecate exposeProxy
and AopContext
for regular execution at some point.
Thanks for the PR, in any case!