I've a web application using the spring(4.2.x) artifacts spring-webmvc, spring-messaging, spring-websocket
I've the below @Enable* annotations in my spring config java class
@EnableWebMvc
@EnableWebSocketMessageBroker
@EnableAsync
@EnableMBeanExport
WebSocket is used for broadcasting messages to browser clients. And there are few async methods annotated with @Async
The application was working fine with spring version 4.2.0.RC3. But when I changed it to the GA release 4.2.0.RELEASE, I get the below exception on startup. If I remove @EnableAsync it works fine, but I need the async functionality.
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.core.task.TaskExecutor] is defined: expected single matching bean but found 4: clientOutboundChannelExecutor,messageBrokerTaskScheduler,clientInboundChannelExecutor,brokerChannelExecutor
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:366)
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor.setBeanFactory(AsyncAnnotationBeanPostProcessor.java:128)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1597)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1565)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201)
org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:228)
org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:682)
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:667)
org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:539)
org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:493)
org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
Spring Boot uses a SimpleAsyncTaskExector to run an async method. This Executor runs by default and it can be overridden at two levels- at the individual method levels or at the application level.
Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method. One interesting aspect in Spring is that the event support in the framework also has support for async processing if necessary.
The @EnableAsync annotation switches on Spring's ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor , since this is the specific method name for which Spring searches.
Actually, Spring @Async uses a background thread pool of it's own. And as long as cancel() method of Future or shutdownNow() of executor service is concerned, Calling executor. shutdownNow() or future. cancel(true) don't stop the ongoing thread immediately.
Add the bean at Spring application context configuration
@Configuration
@EnableAsync
public class AppContext extends WebMvcConfigurationSupport {
@Bean
public Executor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
I suggest that you refer to linuxism.tistory.com/2076
If you declare your executors in XML, then you can create a class and name it TaskExecutor. Then when Spring tries to find the TaskExecutor bean, it will find this one.
@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}
One of your @Configuration
must implement AsyncConfigurer
to specify the particular TaskExecutor
for @Async
methods.
Otherwise it is in confuse which one to choose from the applicationContext
.
Even if it worked with RC3
it doesn't matter that it is correct, hence the bug has been fixed for GA
.
UPDATE
The source code in the AsyncAnnotationBeanPostProcessor
looks like:
Executor executorToUse = this.executor;
if (executorToUse == null) {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
executorToUse = beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
try {
executorToUse = beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, TaskExecutor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
throw new IllegalStateException("More than one TaskExecutor bean exists within the context, " +
"and none is named 'taskExecutor'. Mark one of them as primary or name it " +
"'taskExecutor' (possibly as an alias); or specify the AsyncConfigurer interface " +
"and implement getAsyncExecutor() accordingly.", ex);
}
}
catch (NoSuchBeanDefinitionException ex) {
logger.debug("Could not find default TaskExecutor bean", ex);
// Giving up -> falling back to default executor within the advisor...
}
}
So, I guess before in between moving from RC3 to GA you have had a taskExecutor
bean on the matter.
As we see by you StackTrace there is such a bean already...
For those like myself who are still using old-fashioned XML configuration....
This was working for me before Spring 4.2:
<task:annotation-driven />
<task:executor id="executorA" pool-size="50" />
<task:executor id="executorB" pool-size="100" />
@Async("executorA")
public void executeA() {}
@Async("executorB")
public void executeB() {}
As pointed out by Artem, Spring 4.2 is getting confused about which pool to use for qualifier-less async methods even when you don't have such methods in your application.
To fix it, I used this:
<task:annotation-driven executor="defaultExecutor"/>
<task:executor id="defaultExecutor" pool-size="1" queue-capacity="0"/>
<task:executor id="executorA" pool-size="50" />
<task:executor id="executorB" pool-size="100" />
@Async("executorA")
public void executeA() {}
@Async("executorB")
public void executeB() {}
Note that if you add qualifier-less @Async methods, then those methods will use the defaultExectuor thread-pool:
@Async
public void myDefaultExecute() {}
And, of course, executeA() calls will use executorA thread-pool and executeB() calls will use executorB thread-pool.
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With