java 从 4.2.0.RC3 升级到 4.2.0.RELEASE 时的 Spring Async 问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31816365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:10:19  来源:igfitidea点击:

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

javaspringspring-mvcexecutorservicespring-websocket

提问by Subham Saha

I've a web application using the spring(4.2.x) artifacts spring-webmvc, spring-messaging, spring-websocket

我有一个使用 spring(4.2.x) 工件 spring-webmvc、spring-messaging、spring-websocket 的 Web 应用程序

I've the below @Enable* annotations in my spring config java class

我的 spring 配置 java 类中有以下 @Enable* 注释

@EnableWebMvc
@EnableWebSocketMessageBroker
@EnableAsync
@EnableMBeanExport

WebSocket is used for broadcasting messages to browser clients. And there are few async methods annotated with @Async

WebSocket 用于向浏览器客户端广播消息。并且很少有用@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.

该应用程序在 spring 版本 4.2.0.RC3 上运行良好。但是当我将其更改为 GA 版本 4.2.0.RELEASE 时,我在启动时遇到以下异常。如果我删除 @EnableAsync 它工作正常,但我需要异步功能。

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

采纳答案by Artem Bilan

One of your @Configurationmust implement AsyncConfigurerto specify the particular TaskExecutorfor @Asyncmethods.

@Configuration必须实现之一AsyncConfigurer以指定特定TaskExecutor@Async方法。

Otherwise it is in confuse which one to choose from the applicationContext.

否则就很迷茫选择哪一个applicationContext

Even if it worked with RC3it doesn't matter that it is correct, hence the bug has been fixed for GA.

即使它可以工作RC3,它是否正确也没有关系,因此该错误已针对GA.

UPDATE

更新

The source code in the AsyncAnnotationBeanPostProcessorlooks like:

中的源代码AsyncAnnotationBeanPostProcessor如下所示:

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 taskExecutorbean on the matter.

所以,我猜在从 RC3 迁移到 GA 之前,您taskExecutor对这个问题有所了解。

As we see by you StackTrace there is such a bean already...

正如我们在 StackTrace 中看到的,已经有这样一个 bean 了……

回答by linuxism

Add the bean at Spring application context configuration

在 Spring 应用程序上下文配置中添加 bean

@Configuration
@EnableAsync
public class AppContext extends WebMvcConfigurationSupport {
    @Bean
    public Executor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}

I suggest that you refer to linuxism.tistory.com/2076

我建议你参考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.

如果您在 XML 中声明您的执行程序,那么您可以创建一个类并将其命名为 TaskExecutor。然后当 Spring 试图找到 TaskExecutor bean 时,它会找到这个。

@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}

回答by tnabeel

For those like myself who are still using old-fashioned XML configuration....

对于像我这样仍在使用老式 XML 配置的人......

This was working for me before Spring 4.2:

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

正如 Artem 所指出的那样,即使您的应用程序中没有此类方法,Spring 4.2 也会对使用哪个池用于无限定符的异步方法感到困惑。

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 方法,那么这些方法将使用 defaultExectuor 线程池:

@Async
public void myDefaultExecute() {}

And, of course, executeA() calls will use executorA thread-pool and executeB() calls will use executorB thread-pool.

当然,executeA() 调用将使用 executorA 线程池,executeB() 调用将使用 executorB 线程池。

Hope that helps.

希望有帮助。