This would be even tougher to spot the error if the BeanPostProcessor
instance is from other libraries.
12:43:19.439 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext
- Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException
: Error creating bean with name 'taskExecutor' defined in com.welcome.samples.junit.BeanPostProcessorComponentTest$CustomAsyncConfigurer
: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException
: Failed to instantiate [java.util.concurrent.Executor
]: Illegal arguments to factory method 'getAsyncExecutor'; args: ; nested exception is java.lang.IllegalArgumentException
: object is not an instance of declaring class
class BeanPostProcessorComponentTest {
@Test
void endToEnd() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(CustomAsyncConfigurer.class, ProcessingAsyncConfigurerPostProcessor.class);
ctx.refresh();
ctx.close();
}
@Configuration
public static class CustomAsyncConfigurer implements AsyncConfigurer {
@Bean("taskExecutor")
public Executor getAsyncExecutor() {
return new ConcurrentTaskExecutor();
}
}
public static class ProcessingAsyncConfigurerPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof AsyncConfigurer && !(bean instanceof FullyCustomAsyncConfigurer)) {
return new FullyCustomAsyncConfigurer((AsyncConfigurer) bean);
}
return bean;
}
}
public static class FullyCustomAsyncConfigurer extends AsyncConfigurerSupport {
private AsyncConfigurer delegate;
public FullyCustomAsyncConfigurer(AsyncConfigurer delegate) {
this.delegate = delegate;
}
@Override
public Executor getAsyncExecutor() {
return new Executor() {
@Override
public void execute(Runnable command) {
FullyCustomAsyncConfigurer.this.delegate.getAsyncExecutor().execute(command);
}
};
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return this.delegate.getAsyncUncaughtExceptionHandler();
}
}
}
Comment From: snicoll
I agree that the exception could be improved but the fact that you replace a bean instance (CustomAsyncConfigurer
) by an instance that is not assignable to it is really breaking the post-processor contract. FullyCustomAsyncConfigurer
does not extend CustomAsyncConfigurer
so the override of getAsyncExecutor
is not overriding that method.
Looking at the catch block, it looks like it was primarily designed for providing an error report if the arguments specified for the method invocation has some sort of mismatch. We'll add an extra check there to throw a more specific exception so that we only pay the cost of the check if the exception is thrown.