web-dev-qa-db-ja.com

4.2.0.RC3から4.2.0.RELEASEにアップグレードする際のSpring Asyncの問題

Spring(4.2.x)アーティファクトspring-webmvc、spring-messaging、spring-websocketを使用するWebアプリケーションがあります

Spring config Javaクラスに以下の@ Enable *アノテーションがあります

@EnableWebMvc
@EnableWebSocketMessageBroker
@EnableAsync
@EnableMBeanExport

WebSocketは、ブラウザクライアントにメッセージをブロードキャストするために使用されます。そして、@ Asyncで注釈されたいくつかの非同期メソッドがあります

アプリケーションは、Springバージョン4.2.0.RC3で正常に動作していました。しかし、GA release 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)
17
Subham Saha

@Configurationメソッドに特定のAsyncConfigurerを指定するには、@Asyncの1つが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 Beanを使用していたと思います。

StackTraceで確認したように、すでにそのようなBeanがあります...

10
Artem Bilan

Springアプリケーションコンテキスト構成でBeanを追加する

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

linuxism.tistory.com/2076 を参照することをお勧めします

ExecutorをXMLで宣言する場合は、クラスを作成してTaskExecutorという名前を付けることができます。次に、SpringがTaskExecutor Beanを見つけようとすると、これが見つかります。

@Component
public class TaskExecutor extends SimpleAsyncTaskExecutor {
}
24
linuxism

昔ながらの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スレッドプールを使用します。

お役に立てば幸いです。

1
tnabeel