spring @Async处理异步任务以及常见“异步失效”问题

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

    原文作者:will233
    原文地址: https://segmentfault.com/a/1190000008981884
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞