- Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Affects: <6.0.6>
This is my AsyncConfig with Spring Boot. We use Compile Time Weaving with spring-aspects
@Configuration @EnableAsync(mode = AdviceMode.ASPECTJ) public class AsyncConfig implements AsyncConfigurer { @Bean public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setThreadNamePrefix("AsyncExecutor-"); threadPoolTaskExecutor.setCorePoolSize(15); threadPoolTaskExecutor.setAwaitTerminationSeconds(10); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; } @Bean public Executor taskExecutor() { var delegatingSecurityContextAsyncTaskExecutor = new DelegatingSecurityContextAsyncTaskExecutor(threadPoolTaskExecutor()); return delegatingSecurityContextAsyncTaskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncExceptionHandler(); } }
When I have a test like this:
@Test @DirtiesContext public void test1() { myservice.doSomethingWithAsync() } @Test @DirtiesContext public void test2() { myservice.doSomethingWithAsync() }
I get an error in the second test:
2023-03-29T11:50:09.788+02:00 ERROR 72798 --- [o-auto-6-exec-1] de.kicktipp.core.mailutils.MailHelper : Executor [java.util.concurrent.ThreadPoolExecutor@1294ca02[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 13]] did not accept task: org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect$AbstractAsyncExecutionAspect$1@2283d58f
Debugging it I found that AsyncExecutionAspectSupport holds a reference to executors in line 76:
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16);
This AsyncExecutionAspectSupport is not reloaded when the context is reloaded. It holds a reference to the old executor from the first context. This executor is of course terminated and should not be used.
Workaround:
import org.springframework.beans.factory.DisposableBean; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Map; @Component public class DirtiesContextWithAsyncAspectJFix implements DisposableBean { @Override public void destroy() { try { var asyncSupport = AnnotationAsyncExecutionAspect.aspectOf(); var field = ReflectionUtils.findField(asyncSupport.getClass(), "executors"); assert field != null; field.setAccessible(true); @SuppressWarnings("unchecked") var executors = (Map<Method, AsyncTaskExecutor>) field.get(asyncSupport); executors.clear(); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } }
AsyncExecutionAspectSupport should reset its executors cache on context shutdown.