Подтвердить что ты не робот

Spring Проблема с Async при обновлении с 4.2.0.RC3 до 4.2.0.RELEASE

У меня есть веб-приложение с использованием артефактов spring (4.2.x) spring -webmvc, spring -messaging, spring -websocket

У меня есть аннотации @Enable * в моем spring config 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$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)
4b9b3361

Ответ 1

Один из ваших @Configuration должен реализовать AsyncConfigurer, чтобы указать конкретные методы TaskExecutor для @Async.

В противном случае в путанице можно выбрать один из applicationContext.

Даже если он работал с RC3, не имеет значения, что это правильно, поэтому ошибка была исправлена ​​для GA.

UPDATE

Исходный код в 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

Добавьте 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 {
}

Ответ 3

Для таких, как я, которые все еще используют старую форму 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() {}

Как отметил Артем, 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 без классификатора, то эти методы будут использовать пул потоков по умолчаниюExectuor:

@Async
public void myDefaultExecute() {}

И, конечно, вызовы executeA() будут использовать вызовы threader пула executorA, а вызовы executeB() будут использовать пул потоков executorB.

Надеюсь, что это поможет.