web-dev-qa-db-ja.com

Spring @Asyncが機能しない

@Asyncアノテーションが付けられたクラスの@Serviceメソッドは非同期に呼び出されていません-スレッドをブロックしています。

構成に<task: annotation-driven />があり、メソッドの呼び出しがクラスの外部から来ているため、プロキシがヒットするはずです。コードをステップ実行すると、プロキシは実際にヒットしますが、タスクエグゼキューターでの実行に関連するクラスの近くには行かないようです。

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

このサービスは、実装のメソッドに注釈が付けられた@Asyncを備えたインターフェースとして定義されます(メソッドには注釈付きの@Asyncがあります)。どちらも@Transactionalとマークされていません。

何が間違っていたのでしょうか?

-= UPDATE =-

不思議なことに、アプリではなくapp-servlet.xmlファイルにtask XML要素がある場合にのみ機能しますonly services.xmlファイル、およびそこからサービスをスキャンするコンポーネントを実行する場合。通常、1つのXMLファイルにはコントローラのみが含まれており(それに応じてコンポーネントスキャンが制限されています)、別のXMLファイルにはサービスがあります(もう1つにロードされたコントローラを再スキャンしないようにコンポーネントスキャンが制限されています)ファイル)。

app-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>

app-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"/>
    ...

私の構成で明白に明らかな何かを見逃していますか、それとも構成要素間の微妙な相互作用がありますか?

41

Ryan Stewartによる優れた回答 の助けを借りて、これを理解できました(少なくとも私の特定の問題について)。

つまり、ContextLoaderListener(通常はapplicationContext.xmlから)によってロードされるコンテキストは、DispatcherServlet(通常は_*-servlet.xml_から)によってロードされるコンテキストの親です。両方のコンテキストで_@Async_メソッドが宣言/コンポーネントスキャンされたBeanがある場合、子コンテキスト(DispatcherServlet)のバージョンが親コンテキスト(ContextLoaderListener)のバージョンをオーバーライドします。 。 _*-servlet.xml_のコンポーネントスキャンからそのコンポーネントを除外することでこれを検証しました-現在は期待どおりに動作します。

28
ach

私にとっての解決策は、@EnableAsync注釈付きクラスに@Configurationを追加することでした。

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

}

bla.packageアノテーション付きメソッドを持つパッケージ@Asyncのクラスは、実際にそれらを非同期的に呼び出すことができます。

26
Shivan Dragon
  1. この属性をサポートするすべてのproxy-target-class="true"要素に<*:annotation-driven/>を追加してみてください。
  2. @Asyncアノテーションが付けられたメソッドがパブリックかどうかを確認します。
11

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

  1. @Asyncアノテーションが付けられたメソッドがパブリックかどうかを確認します。

Springチュートリアルの別の有用な情報 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
  }

}

編集:静的に呼び出すことも非同期で実行しないようです。

お役に立てれば。

11
typoerrpr

まず、.xml configは次のようになります。

<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" />

(はい、スケジューラー数とエグゼキュータースレッドプールサイズは構成可能です)

または、デフォルトを使用します:

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

次に、@Asyncメソッドはパブリックです。

4
coderz

チュートリアルに従ってください async-method tutorial code 私の問題のソースは、注釈付き@Asyncメソッドを持つBeanがプロキシにラップされて作成されていなかったことです。掘り始めて、次のようなメッセージがあることに気づきました

Bean 'NameOfTheBean'は、すべてのBeanPostProcessorsによって処理される資格がありません(例:自動プロキシの資格がありません)

この問題に関する here の応答と、基本的にBeanPostProcessorsがすべてのBeanに必要であることがわかります。したがって、ここで注入されたすべてのBeanとその依存関係は除外されます。豆のサイクル。したがって、これを引き起こしているBeanPostProcessorを特定し、その内部でBeanを使用または作成しないでください。

私の場合、私はこの構成を持っていました

@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であり、クラスを拡張し、いくつかの非機能に関与するBeanをインストールまたは調整する機能をオーバーライドする@Configurationというパターンが常にあるため、あなたはそれを実現します。 Webサービスやセキュリティなどの機能。

前述の例では、addInterceptorsをオーバーライドしてインターセプターBeanを追加する必要があるため、DefaultPayloadLoggingInterceptor内で@Asyncのような注釈を使用している場合は機能しません。解決策は何ですか? 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がジョブを実行した後に実行されます。 setupInterceptors関数は、パーティが終了するとインターセプターBeanをインストールするときに実行されます。このユースケースは、セキュリティなどのケースに外挿することができます。

結論:

  • 特定の関数を自動的に実行するクラスから拡張する@Configurationを使用しており、それらをオーバーライドする場合は、おそらくBeanPostProcessorの内部にいるので、そこにBeanを注入せず、AOPの動作を使用しようとします。動作し、Springがコンソールに前述のメッセージを表示します。これらの場合、Beanではなくオブジェクトを使用します(new句を使用)。
  • Beanを使用して、最後にセットアップするBeanをどのクラスが保持しているかについて調べる必要がある場合は、@Autowiredそれを追加し、以前と同じようにそれらのBeanを追加します。

これにより時間を節約できることを願っています。

1
EliuX

Asyncを機能させるには3行のコードが必要です

  1. applicationContext.xmlで
  2. クラスレベルで@EnableAsync
  3. メソッドレベルで@Async

@Service @EnableAsync public myClass {

@Async public void myMethod(){

}

1
vsingh

@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();
  }
}

ソース

1

以下を試してください。1. configでThreadPoolTaskExecutorのBeanを作成します

@Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }

2. @Asyncが使用されるサービスメソッドで追加

@Async("threadPoolTaskExecutor")
    public void asyncMethod(){
    //do something
    }

これで@Asyncが機能するはずです。

0
Ravik

非同期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;
    }
}

私はこの状況でこの問題を克服しました。

0
Cheng