从4.2.0.RC3升级到4.2.0.RELEASE时的Spring异步问题

19

我有一个使用Spring(4.2.x)的Web应用程序,使用了spring-webmvc、spring-messaging和spring-websocket的构件。

在我的Spring配置Java类中,我使用了以下 @Enable* 注释:

@EnableWebMvc
@EnableWebSocketMessageBroker
@EnableAsync
@EnableMBeanExport

WebSocket 用于向浏览器客户端广播消息。有一些使用 @Async 注释的异步方法。

这个应用在 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$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)
4个回答

27

将Bean添加到Spring应用程序上下文配置中

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

我建议您参考linuxism.tistory.com/2076

如果您在XML中声明了执行器,那么可以创建一个类并将其命名为TaskExecutor。当Spring尝试查找TaskExecutor bean时,它将找到这个类。

@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}

11

你的其中一个@Configuration必须实现AsyncConfigurer来指定特定的TaskExecutor用于@Async方法。

否则会让applicationContext混淆选择哪一个。

即使它在RC3中能工作,也不意味着它是正确的,因此该错误已在GA中得到修复。

更新

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

所以我猜在从RC3到GA的过程中,您在此事上已经有一个taskExecutor bean了。

根据您提供的StackTrace,我们可以看到已经存在这样一个bean...


2
在RC3中哪一个是默认的,它们之间有什么区别? - EpicPandaForce
1
@Artem 在参考文档中没有提到为@Async方法指定特定的TaskExecutor。如果未指定,则我认为Spring应该提供默认的TaskExecutor - Subham Saha
请在我的回答中查看UPDATE - Artem Bilan
@ArtemBilan 谢谢,我在一个 @Configuration 类中扩展了 AsyncConfigurerSupport - Subham Saha

2

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

在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() {}

正如 Artem 指出的那样,即使您的应用程序中没有不带限定符的异步方法,Spring 4.2 仍会对使用哪个池子感到困惑。

要解决这个问题,我使用了以下方法:

<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() {}

请注意,如果您添加没有限定符的@Async方法,则这些方法将使用默认的Executor线程池:
@Async
public void myDefaultExecute() {}

当然,executeA()调用将使用executorA线程池,而executeB()调用将使用executorB线程池。
希望这可以帮助您。

0

提供自定义的bean名称并通过该名称注入bean解决了我的问题。

配置Bean创建:

@Configuration
@EnableAsync
public class AsyncConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(AsyncConfiguration.class);

    @Bean(name = "asyncTaskExecutor")
    public Executor asyncTaskExecutor() {
        LOGGER.debug("Creating Async Task Executor");
        final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Executor-");
        executor.initialize();
        return executor;
    }

}

Bean注入。

 @Async("asyncTaskExecutor")
 public Future<ATIQueryResult> processApiMessage(String msisdn) {
 
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接