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.
Affects: \<6.0.6>
This is my AsyncConfig with Spring Boot. We use Compile Time Weaving with spring-aspects
When I have a test like this:
I get an error in the second test:
Debugging it I found that AsyncExecutionAspectSupport holds a reference to executors in line 76:
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:
AsyncExecutionAspectSupport should reset its executors cache on context shutdown.