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)
Ответы
Ответ 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.
Надеюсь, что это поможет.