2011-07-07 64 views
36

@Async@Service中的方法-annotated類不是異步調用 - 它阻塞了線程。Spring @Async不工作

我在我的配置中有<task: annotation-driven />,並且對該方法的調用來自類之外,因此代理應該被擊中。當我遍歷代碼時,代理確實被擊中了,但它似乎並不在任何與任務執行器中運行相關的類中。

我已經在AsyncExecutionInterceptor中放置了斷點,並且它們從未受到影響。我已調試到AsyncAnnotationBeanPostProcessor,並可以看到獲得應用的建議。

該服務也被定義爲一個接口(其中註釋的方法爲@Async),實現的方法也註釋爲@Async。都沒有標記@Transactional

任何想法可能出了什麼問題?

- =更新= -

奇怪的是,它的工作原理只有時,我有我的應用程序-servlet.xml文件我task XML元素,而不是在我的應用程序-services.xml文件,如果我也從那裏做服務的組件掃描。通常情況下,我有一個XML文件,其中只有控制器(並相應地限制組件掃描),另一個文件中包含服務(再次限制組件掃描,以便它不會重新掃描其他加載的控制器文件)。

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

我這麼想昭然若揭在我的配置中很明顯,還是在配置元素之間存在一些微妙的相互作用?

+1

你確定你使用Spring''@ ASync'類型,而不是另一個庫嗎? – skaffman

+0

'import org.springframework.scheduling.annotation.Async;'是我正在使用的。我已經看到,當代理被調用時,它認爲聲明的類(接口)沒有任何類或方法級別的註釋 - 這是一個謊言! –

+4

只是爲了仔細檢查,該方法是外部調用,而不是從服務內部正確?並且您正在調用由Spring構建的服務的方法,而不是您已經「新」編輯的服務? – Pace

回答

26

在此excellent answer by Ryan Stewart的幫助下,我能夠弄清楚這一點(至少對於我的具體問題)。

簡而言之,由ContextLoaderListener(通常來自applicationContext.xml)加載的上下文是由DispatcherServlet(通常從*-servlet.xml)加載的上下文的父代。如果您在上下文中聲明/組件掃描了@Async方法的bean,則子上下文(DispatcherServlet)中的版本將覆蓋父上下文(ContextLoaderListener)中的版本。我通過從*-servlet.xml中的組件掃描中排除該組件來驗證這一點 - 現在按預期工作。

+0

可悲的是我無法驗證此修復程序,因爲我沒有訪問代碼的權限更多,但我幾乎可以肯定這是問題所在。根源和網絡環境之間的關係一直在讓我煩躁。非常感謝你和瑞恩斯圖爾特! –

+0

我一直在遇到同樣的問題,這正是解決方案 - 感謝張貼,ach。 – SteveT

+0

請告訴我什麼是解決方案。我也有同樣的問題。我能做些什麼來解決這個問題? –

11
  1. 嘗試將proxy-target-class="true"添加到支持該屬性的所有<*:annotation-driven/>元素。
  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註釋的方法是否公開。

從春天教程https://spring.io/guides/gs/async-method/另一個有用的信息:

創建FacebookLookupService類的本地實例不 允許查找網頁的方法來異步運行。它必須在 @Configuration類中創建或由@ComponentScan拾取。

這意味着,如果你有一個靜態方法Foo.bar()調用它以這種方式不會在異步執行它,即使它與@Async註解。您必須使用@Component註釋Foo,並在調用類中獲取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 
    } 

} 

編輯:好像稱這是靜態也沒有執行它在異步。

希望這會有所幫助。

4

首先讓你.xml的配置是這樣的:

<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方法是公開的。

1

我意識到遵循教程async-method tutorial code我的問題來源是:註釋爲@Async方法的bean未被創建包裝在代理中。 我開始挖掘,並意識到,有一條消息說

豆「NameOfTheBean」不符合所有 BeanPostProcessor的得到處理(例如:不符合自動代理)

你可以看到有關此問題的here響應,並且其基本上每個Bean都需要BeanPostProcessors,因此,此處注入的每個Bean及其依賴項都將被排除,以供其他BeanPostProcessors稍後處理,因爲它損壞了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,你知道它,因爲總有一個模式:@Configuration,擴展類和重寫它的一些功能來安裝或調整涉及豆在一些非功能性的功能,如網絡服務或安全。

在上述示例中,您必須重寫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完成他們的工作後運行。當該方結束並安裝攔截器bean時,setupInterceptors函數將運行。這個用例可以外推到像Security這樣的例子。

結論:

  • 如果您正在使用從自動運行某個給定的功能,你重寫他們一些類延伸的@Configuration,你可能是一個BeanPostProcessor內,所以不存在注入豆類和嘗試使用AOP的行爲,因爲它不會工作,你會看到Spring通過控制檯中的前述消息告訴你。在這些情況下,不要使用bean而是使用對象(使用new子句)。
  • 如果您需要使用beans digg關於哪個類攜帶最後要設置的bean,@Autowired並添加這些bean,就像我以前一樣。

我希望這可以爲您節省一些時間。

0

@Async不能與生命週期回調(如@PostConstruct)結合使用。爲了異步初始化Spring bean,您現在必須使用單獨的初始化Spring bean,然後在目標上調用@Async註釋方法。

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; 
    } 
} 

我克服這個問題,這種局面。