spring @Async處理非同步任務以及常見“非同步失效”問題

will233發表於2017-04-07

情景:
在某些業務過程,對於一些耗時較長,使用者無需等待的過程,可以使用非同步方法進行處理。使用spring @Async可以實現非同步方法

實現:

1.在spring配置檔案中加入非同步支援

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
    http://www.springframework.org/schema/tx    
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd   
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-4.0.xsd   
    http://www.springframework.org/schema/mvc   
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-4.0.xsd">

    <context:annotation-config />
    <!--掃描註解 -->
    <context:component-scan base-package="com.xx.yy.*" />
    <!-- 支援非同步方法執行 -->
    <task:annotation-driven />
</beans>

2.寫非同步方法

@Component
public class AsyncTaskTest {
    
    @Async
    public static void doSomething()throws Exception{
        Thread.sleep(10000);
        System.out.println("begin doSomthing....");
        Thread.sleep(10000);
        System.out.println("end doSomthing....");
    }
}

3.呼叫非同步方法

@Controller
public class DebugController {

    @Autowired
    AsyncTaskTest asyncTask;
    
    @RequestMapping("/main")
    public String asynTest() {        
        try {
            System.out.println("我開始執行了!");
            asyncTask.doSomething();            
            System.out.println("我執行結束了!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "main";
    }
}

4.常見問題

做了1、2、3程式不一定能正確執行非同步方法。因為非同步方法有可能因為某些問題而失效。常見的非同步失效原因有:
1、非同步方法和呼叫類不要在同一個類中
2、註解掃描時,要注意過濾,避免重複例項化,因為存在覆蓋問題,@Async就失效了

問題2舉例,如果除了applicationContext.xml 中配置了包掃描外,還在dispatcher-Servlet.xml中還配置了包掃描。則會出現非同步失效問題。

<context:component-scan base-package="com.xx.yy.*" />

解決方案:
1.在dispatcher-Servlet.xml 中指定掃描某個包

<context:component-scan base-package="com.xx.yy.controller" >
</context:component-scan>

只有@Async非同步方法不再該包下才能避免被重複掃描。

2.指定掃描包下的某個標籤

<context:component-scan base-package="com.xx.yy.*"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

實際上<context:component-scan> 有個屬性use-default-filters它的預設值為true,會掃描指定包下的全部的標有@Component的類,並註冊成bean.也就是@Component的子註解@Service,@Reposity等。所以如果僅僅是在配置檔案中這麼寫:

<context:component-scan base-package="com.xx.yy.*" >
    <context:include-filter type="annotation"
        expression="org.springframework.stereotype.Controller" />
</context:component-scan>

Use-default-filter此時為true那麼會對base-package包或者子包下的所有的進行Java類進行掃描,並把@Component的類的java類註冊成bean,所以還是不能避免非同步方法所在類被重複掃描。
如果需要指定一些包掃描,一些包不掃描,則需要修改Use-dafault-filter的預設值。
Use-dafault-filters=”false”的情況下:<context:exclude-filter>指定的不掃描,<context:include-filter>指定的掃描。

<context:component-scan base-package="com.xx.yy.*"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

—–end—–

相關文章