情景:
在某些业务过程,对于一些耗时较长,用户无需等待的过程,可以使用异步方法进行处理。使用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—–