@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"/>
...
私の構成で明白に明らかな何かを見逃していますか、それとも構成要素間の微妙な相互作用がありますか?
Ryan Stewartによる優れた回答 の助けを借りて、これを理解できました(少なくとも私の特定の問題について)。
つまり、ContextLoaderListener
(通常はapplicationContext.xmlから)によってロードされるコンテキストは、DispatcherServlet
(通常は_*-servlet.xml
_から)によってロードされるコンテキストの親です。両方のコンテキストで_@Async
_メソッドが宣言/コンポーネントスキャンされたBeanがある場合、子コンテキスト(DispatcherServlet
)のバージョンが親コンテキスト(ContextLoaderListener
)のバージョンをオーバーライドします。 。 _*-servlet.xml
_のコンポーネントスキャンからそのコンポーネントを除外することでこれを検証しました-現在は期待どおりに動作します。
私にとっての解決策は、@EnableAsync
注釈付きクラスに@Configuration
を追加することでした。
@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {
}
bla.package
アノテーション付きメソッドを持つパッケージ@Async
のクラスは、実際にそれらを非同期的に呼び出すことができます。
proxy-target-class="true"
要素に<*:annotation-driven/>
を追加してみてください。@Async
アノテーションが付けられたメソッドがパブリックかどうかを確認します。JiříVypědříkの答えは私の問題を解決しました。具体的には、
- @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
}
}
編集:静的に呼び出すことも非同期で実行しないようです。
お役に立てれば。
まず、.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
メソッドはパブリックです。
チュートリアルに従ってください 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をインストールするときに実行されます。このユースケースは、セキュリティなどのケースに外挿することができます。
結論:
BeanPostProcessor
の内部にいるので、そこにBeanを注入せず、AOPの動作を使用しようとします。動作し、Springがコンソールに前述のメッセージを表示します。これらの場合、Beanではなくオブジェクトを使用します(new
句を使用)。@Autowired
それを追加し、以前と同じようにそれらのBeanを追加します。これにより時間を節約できることを願っています。
Asyncを機能させるには3行のコードが必要です
@Service @EnableAsync public myClass {
@Async public void myMethod(){
}
@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. configでThreadPoolTaskExecutor
のBeanを作成します
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
2. @Asyncが使用されるサービスメソッドで追加
@Async("threadPoolTaskExecutor")
public void asyncMethod(){
//do something
}
これで@Asyncが機能するはずです。
非同期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;
}
}
私はこの状況でこの問題を克服しました。