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

本文介绍如何使用Spring框架实现异步方法,包括配置支持、编写及调用异步方法,并解决常见问题,如异步方法失效等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Java中使用`@Async`注解实现异步方法调用时,可能会遇到异步方法失效问题。这种失效通常与Spring AOP代理机制的限制有关,以下是常见导致`@Async`失效的原因及对应的解决方案。 ### 1. 异步方法必须为 `public` 修饰 `@Async`注解的方法必须声明为 `public`,否则Spring AOP将无法为其生成代理,导致异步调用不生效。如果方法被声明为 `protected`、`private` 或者包级私有,则不会触发异步行为[^2]。 ```java @Service public class MyService { @Async public void asyncMethod() { // 异步执行的逻辑 } public void callAsyncMethod() { asyncMethod(); // 正确调用,因为 asyncMethod 是 public 的 } } ``` ### 2. 同类中直接调用异步方法导致失效 在同一个类中,如果一个普通方法直接调用带有`@Async`的方法,由于Spring AOP代理机制的限制,该调用不会经过代理对象,因此异步调用不会生效。解决方法包括: - **方式1:将异步方法拆分到不同类中** 将异步方法定义在另一个服务类中,并通过依赖注入调用,这样可以确保调用是通过代理对象完成的。 - **方式2:通过代理对象调用** 使用`AopContext.currentProxy()`获取当前类的代理对象,再通过代理对象调用异步方法。 ```java @Service public class MyService { @Autowired private MyService selfProxy; @Async public void asyncMethod() { // 异步执行的逻辑 } public void callAsyncMethod() { selfProxy.asyncMethod(); // 通过代理对象调用,确保异步生效 } } ``` ### 3. `final` 方法导致异步失效 如果将`@Async`注解应用于`final`方法上,由于Spring AOP无法对`final`方法进行动态代理(特别是在基于CGLIB的代理机制中),会导致异步调用失效。应避免在`final`方法上使用`@Async`注解[^3]。 ```java @Service public class UserService { public void test() { async("test"); // 调用不会异步执行,因为 async 方法是 final 的 } @Async public void async(String value) { // 应避免使用 final 修饰 // 异步逻辑 } } ``` ### 4. 未正确开启异步支持 确保在配置类或启动类上添加了`@EnableAsync`注解,以启用Spring异步任务支持。否则,即使使用了`@Async`注解,也不会生效[^4]。 ```java @Configuration @EnableAsync public class AsyncConfig { // 可以在这里自定义线程池等配置 } ``` ### 5. 线程池配置不合理 默认情况下,Spring使用一个简单的线程池来执行异步任务。如果任务量较大或任务执行时间较长,可能导致线程资源耗尽。可以通过自定义线程池来优化异步任务的执行效率。 ```java @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-Executor-"); executor.initialize(); return executor; } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值