2011-07-07 29 views
36

@Serviceアニメーション化されたクラスの@Asyncメソッドは、非同期的に呼び出されていません。スレッドをブロックしています。Spring @Async Not Working

私の設定には<task: annotation-driven />があり、メソッドの呼び出しはクラス外から来ているので、プロキシにヒットする必要があります。コードをステップ実行すると、プロキシは実際にヒットしますが、タスクエグゼキュータでの実行に関連するクラスの近くにはありません。

私はAsyncExecutionInterceptorにブレークポイントを設定しましたが、決してヒットしません。私はAsyncAnnotationBeanPostProcessorにデバッグし、アドバイスが適用されるのを見ることができます。

サービスは、@Asyncというアノテーションが付けられた実装のメソッドで、(適切な対策のために、@Asyncアノテーションを付けたメソッドで)インターフェイスとして定義されています。どちらも、@Transactionalと表示されていません。

何が間違っている可能性がありますか?

- = UPDATE = -

不思議なことに、それは私が私のアプリ-servlet.xmlファイルではなく、私のアプリ-services.xmlファイルで私のtask XML要素を持っているのみときに動作し、私の場合私のコンポーネントはそこからサービスをスキャンします。通常、私はその中にコントローラだけを持つXMLファイルを持っています(それに応じてコンポーネントスキャンを制限します)。そしてもう一つはコンポーネントスキャンを制限して、他のXMLファイルを読み込みませんファイル)。

アプリ-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jee="http://www.springframework.org/schema/jee" 
xmlns:mvc="http://www.springframework.org/schema/mvc" 
xmlns:webflow="http://www.springframework.org/schema/webflow-config" 
xmlns:task="http://www.springframework.org/schema/task" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
    http://www.springframework.org/schema/task 
    http://www.springframework.org/schema/task/spring-task-3.0.xsd 
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd 
    http://www.springframework.org/schema/jee 
    http://www.springframework.org/schema/jee/spring-jee-3.0.xsd" 
> 
<task:annotation-driven executor="executor" /> 
<task:executor id="executor" pool-size="7"/> 

<!-- Enable controller annotations --> 
<context:component-scan base-package="com.package.store"> 
    <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> --> 
</context:component-scan> 

<tx:annotation-driven/> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="sessionFactory"/> 
</bean> 

<mvc:annotation-driven conversion-service="conversionService" /> 

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
    <property name="prefix" value="/WEB-INF/jsp/" /> 
    <property name="suffix" value=".jsp" /> 
</bean> 

アプリ-services.xmlの(ここで指定したときに動作しません)

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/task 
     http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 

    <!-- Set up Spring to scan through various packages to find annotated classes --> 
    <context:component-scan base-package="com.package.store"> 
     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
    </context:component-scan> 

    <task:annotation-driven executor="han" /> 
    <task:executor id="han" pool-size="6"/> 
    ... 

私は紛れも何かが足りません明らかに私の設定では、または設定要素間の微妙な相互作用が進行中ですか?

+1

あなたは別のライブラリのものではなく、春の '@ ASync'タイプを使用していますか? – skaffman

+0

'import org.springframework.scheduling.annotation.Async;'は私が使っているものです。私は、プロキシが呼び出されると、宣言されたクラス(インターフェイス)にクラスまたはメソッドレベルの注釈がないと思っています。これはうそです! –

+4

ダブルチェックするだけで、メソッドはサービス内からではなく、外部から呼び出されていますか?そして、あなたは "新しい"エドアップしたサービスではなく、春から構築されたサービスのメソッドを呼び出していますか? – Pace

答えて

26

このexcellent answer by Ryan Stewartの助けを借りて、私はこれを(少なくとも私の特定の問題については)理解することができました。つまり、ContextLoaderListener(通常はapplicationContext.xml)によってロードされるコンテキストは、DispatcherServlet(一般的には*-servlet.xml)によってロードされるコンテキストの親です。両方のコンテキストで@Asyncメソッドが宣言された/コンポーネントスキャンされたBeanを持つ場合、子コンテキスト(DispatcherServlet)のバージョンは親コンテキストのバージョン(ContextLoaderListener)を上書きします。 *-servlet.xmlのコンポーネントスキャンからそのコンポーネントを除外してこれを確認しました。これは現在、期待どおりに動作します。私にとって

+0

を参照してください。残念ながら、このコードにアクセスできないため、この修正を確認することはできませんこれ以上問題はないと私はほとんど確信しています。ルートコンテキストとウェブコンテキストの関係は、しばらく私を悩ませていました。あなたとライアン・スチュワートに感謝します! –

+0

私は同じ問題を抱えてきました。これはまさに解決策でした。 – SteveT

+0

解決策を教えてください。私も同じ問題があります。この問題を解決するにはどうすればよいですか? –

11
  1. この属性をサポートするすべての<*:annotation-driven/>要素にproxy-target-class="true"を追加してみてください。
  2. @Asyncで注釈を付けたメソッドが公開されているかどうかを確認してください。
21

ソリューションは、私の@Configuration注釈付きクラスに@EnableAsyncを追加しました:

@Configuration 
@ComponentScan("bla.package") 
@EnableAsync 
public class BlaConfiguration { 

} 

@Async注釈付きメソッドを持つパッケージbla.packageでのクラスは本当に彼らが非同期的に呼び出されることができます。

+0

これも私の場合でした! tanx – azerafati

+0

魅力的な作品! –

+0

ありがとう、これも私のためにそれを解決! –

8

JiříVypědříkの答えが私の問題を解決しました。具体的には、

  1. @Asyncで注釈を付けたメソッドがpublicであることを確認してください。

春のチュートリアルhttps://spring.io/guides/gs/async-method/から別の有用な情報:FacebookLookupServiceクラスのローカルインスタンスを作成

がfindPageメソッドは非同期に実行することはできませ ありません。それは@Configurationクラスの中で の中に作成するか、@ComponentScanによって取得する必要があります。これが何を意味するのか

あなたは、静的メソッドFoo.bar()を持っていたならば、そのようにそれを呼び出すと、それは@Asyncで注釈された場合でも、非同期でそれを実行しないことです。あなたはFooに@Componentを注釈を付けなければならなくなり、呼び出し側のクラスではFooの@Autowiredインスタンスを取得します。

すなわち、あなたはFooクラスでのアノテーション付きメソッドのバーがある場合:あなたの呼び出し側のクラスで

@Component 
class Foo { 
    @Async 
    public static void bar(){ /* ... */ } 

    @Async 
    public void bar2(){ /* ... */ } 
} 

アン:

class Test { 

    @Autowired Foo foo; 

    public test(){ 
    Foo.bar(); // Not async 
    foo.bar(); // Not async 
    foo.bar2(); // Async 
    } 

} 

編集を:また、静的にそれを呼び出すように思えるが、それを実行しません。非同期で

これが役に立ちます。

それとも、デフォルトを使用する(はい、スケジューラ・カウントとエグゼキュータスレッドプールのサイズは設定可能です)

<task:scheduler id="myScheduler" pool-size="10" /> 
<task:executor id="myExecutor" pool-size="10" /> 
<task:annotation-driven executor="myExecutor" scheduler="myScheduler" proxy-target-class="true" /> 

4

まず、あなたの.xml設定は以下のように見えるように

<!-- enable task annotation to support @Async, @Scheduled, ... --> 
<task:annotation-driven /> 

は、第二に作ります確かに@Asyncメソッドはpublicです。

1

チュートリアルasync-method tutorial codeの後に、問題のソースがありました。注釈付き@Asyncメソッドを持つBeanは、プロキシでラップされて作成されていませんでした。 私が掘り始め、

ビーンズNameOfTheBeanは '(例:自動プロキシの対象とならない)すべて BeanPostProcessorsによって処理されたばかりの対象ではありませんというメッセージがあったことに気づいたあなたを

この問題についてのhereの応答を見ることができます。基本的にBeanごとにBeanPostProcessorsが必要です。ここで注入されたすべてのBeanとその依存関係は、後で他のBeanPostProcessorsによって処理されないように除外されます。だから、これを引き起こしているBeanPostProcessorを特定し、内部でBeanを使用したり作成したりしないでください。私の場合は

iは

@EnableWs 
@Configuration 
public class WebServiceConfig extends WsConfigurerAdapter { 

    @Autowired 
    private Wss4jSecurityInterceptor securityInterceptor; 

    @Autowired 
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor; 

    @Override 
    public void addInterceptors(List<EndpointInterceptor> interceptors) { 
     interceptors.add(securityInterceptor); 
     interceptors.add(payloadLoggingInterceptor); 
    } 
} 

WsConfigurerAdapterが実際BeanPostProcessorでこの設定を持っていたし、いつものパターンがあるので、あなたはそれを実現:@Configurationクラスを拡張し、その一部がインストールまたは関与豆を微調整する機能上書きWebサービスやセキュリティのような非機能的な機能では、

上記の例では、addInterceptorsを追加してインターセプタBeanを追加する必要があります。@Asyncのような注釈を使用している場合は、DefaultPayloadLoggingInterceptorは機能しません。解決策は何ですか?開始するにはWsConfigurerAdapterに乗る。 ちょっと掘り下げたあと、最後にPayloadRootAnnotationMethodEndpointMappingという名前のクラスがすべての有効なインターセプターを持っていたことに気がついたので、私は手動で関数をオーバーライドしました。

@EnableWs 
@Configuration 
public class WebServiceConfig { 

    @Autowired 
    private Wss4jSecurityInterceptor securityInterceptor; 

    @Autowired 
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor; 

    @Autowired 
    public void setupInterceptors(PayloadRootAnnotationMethodEndpointMapping endpointMapping) { 
     EndpointInterceptor[] interceptors = { 
       securityInterceptor, 
       payloadLoggingInterceptor 
     }; 

     endpointMapping.setInterceptors(interceptors); 
    } 
} 

BeanPostProcessorが完了した後に実行されます。そのパーティーが終了し、インターセプタBeanをインストールすると、setupInterceptors関数が実行されます。このユースケースは、セキュリティなどのケースに外挿することができます。

結論:

  • あなたは自動的にいくつかの特定の機能を実行し、いくつかのクラスから延びる@Configurationを使用していて、それらをオーバーライドする場合は、あなたがBeanPostProcessorの内側におそらくあるので、そこに豆を注入し、しようといけません動作しないのでAOPの動作を使用してください。そして、あなたが前に述べたメッセージをコンソールに表示してくれます。そのような場合には、Beanではなくオブジェクト(new節を使用)を使用しないでください。
  • 最後に設定したい豆をどのクラスが運んでいるかについて豆diggを使用する必要がある場合は、@Autowiredそれを以前のように追加してください。

これにより、時間が節約されることを願っています。

0

@Asyncを@PostConstructなどのライフサイクルコールバックと組み合わせて使用​​することはできません。 Spring Beanを非同期的に初期化するには、現在、ターゲット上で@Async注釈付きメソッドを呼び出す個別のSpring Beanを初期化する必要があります。

public class SampleBeanImpl implements SampleBean { 

    @Async 
    void doSomething() { … } 
} 


public class SampleBeanInititalizer { 

    private final SampleBean bean; 

    public SampleBeanInitializer(SampleBean bean) { 
    this.bean = bean; 
    } 

    @PostConstruct 
    public void initialize() { 
    bean.doSomething(); 
    } 
} 

source

+0

「現在」と言えば、どのバージョンのSpringを参照していますか? –

+0

これはSpring 3.0.xのドキュメントの引用ですが、私はSpring 4.3.6で同じことに直面しており、このように動作させることができました –

0

非同期Beanの独立したSpring構成を書き込みます。例えば

@Configuration 
@ComponentScan(basePackages="xxxxxxxxxxxxxxxxxxxxx") 
@EnableAsync 
public class AsyncConfig { 

    /** 
    * used by asynchronous event listener. 
    * @return 
    */ 
    @Bean(name = "asynchronousListenerExecutor") 
    public Executor createAsynchronousListenerExecutor() { 
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
     executor.setMaxPoolSize(100); 
     executor.initialize(); 
     return executor; 
    } 
} 

私はこのような状況でこの問題を克服します。