Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Async issue when upgrading from 4.2.0.RC3 to 4.2.0.RELEASE

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)
like image 564
Subham Saha Avatar asked Aug 04 '15 18:08

Subham Saha


People also ask

Is Spring boot async by default?

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.

What does @async annotation do?

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.

What does @async do in Spring?

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.

How do I turn off async in Spring boot?

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.


3 Answers

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 {
}
like image 90
linuxism Avatar answered Oct 23 '22 08:10

linuxism


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...

like image 11
Artem Bilan Avatar answered Oct 23 '22 06:10

Artem Bilan


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.

like image 2
tnabeel Avatar answered Oct 23 '22 07:10

tnabeel