J'ai une application Web utilisant les artefacts Spring (4.2.x) spring-webmvc, spring-messaging, spring-websocket
J'ai les annotations @ Activer * ci-dessous dans ma classe Java Spring Config
@EnableWebMvc
@EnableWebSocketMessageBroker
@EnableAsync
@EnableMBeanExport
WebSocket est utilisé pour la diffusion de messages vers les clients du navigateur . Et il existe peu de méthodes asynchrones annotées avec @Async
L'application fonctionnait correctement avec la version à ressort 4.2.0.RC3. Mais lorsque je l'ai modifiée en GA version 4.2.0.RELEASE, l'exception ci-dessous est générée au démarrage. Si je supprime @EnableAsync, cela fonctionne bien, mais j'ai besoin de la fonctionnalité async.
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)
Un de vos @Configuration
doit implémenter AsyncConfigurer
pour spécifier la TaskExecutor
pour les méthodes @Async
.
Sinon, il est difficile de choisir lequel choisir parmi applicationContext
.
Même si cela fonctionnait avec RC3
, peu importe qu'il soit correct, le bogue a donc été corrigé pour GA
.
METTRE À JOUR
Le code source dans la AsyncAnnotationBeanPostProcessor
ressemble à ceci:
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...
}
}
Je suppose donc qu'avant de passer de la RC3 à la position GA, vous avez eu une idée taskExecutor
.
Comme on voit par toi StackTrace il y a déjà un tel haricot ...
Ajouter le bean à la configuration du contexte d'application Spring
@Configuration
@EnableAsync
public class AppContext extends WebMvcConfigurationSupport {
@Bean
public Executor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
Je suggère que vous vous référiez à linuxism.tistory.com/2076
Si vous déclarez vos exécuteurs en XML, vous pouvez créer une classe et la nommer TaskExecutor. Ensuite, lorsque Spring essaiera de trouver le bean TaskExecutor, il le trouvera.
@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}
Pour ceux qui, comme moi, utilisent encore une configuration XML à l'ancienne ....
Cela fonctionnait pour moi avant le printemps 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() {}
Comme l'a souligné Artem, Spring 4.2 commence à ne pas savoir quel pool utiliser pour les méthodes asynchrones sans qualificateur, même si vous n'en avez pas dans votre application.
Pour résoudre ce problème, j'ai utilisé ceci:
<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() {}
Notez que si vous ajoutez des méthodes @Async sans qualificateur, ces méthodes utiliseront le pool de threads defaultExectuor:
@Async
public void myDefaultExecute() {}
Et, bien sûr, les appels executeA () utiliseront executorA-pool de threads et executeB () utiliseront executorB-pool.
J'espère que cela pourra aider.