Spring 从 4.2.0.RC3 升级到 4.2 时出现异步问题。0.RELEASE

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

我有一个使用 spring(4.2.x) 工件的 Web 应用程序 spring-webmvc,spring-消息,spring-网络套接字

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

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

您的 @Configuration 之一必须实现 AsyncConfigurer 才能为 @Async 方法指定特定的 TaskExecutor

不然在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 的想法。

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

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

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

建议参考linuxism.tistory.com/2076

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

@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}

对于像我这样仍在使用老式 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 方法,那么这些方法将使用 defaultExectuor 线程池:

@Async
public void myDefaultExecute() {}

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

希望对您有所帮助。