情景:
在某些業務過程,對於一些耗時較長,使用者無需等待的過程,可以使用非同步方法進行處理。使用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—–