Skip to main content
 首页 » 编程设计

spring 使用@Async注解实现异步执行

2022年07月19日152mfryf

spring 使用@Async注解实现异步执行

文本我们介绍spring中使用@Async注解实现异步执行功能。通过@Async注解的bean方法将在独立的线程中执行,即执行者无需等待调用方法完成。
Spring框架中的事件机制也支持异步处理。

启用异步支持

我们使用javaConfig方式配置启用异步支持,通过在配置类上增加注解@EnableAsync:

@Configuration 
@EnableAsync 
public class SpringAsyncConfig { ... }

示例中注解可以满足需求,同时还有几个参数:

annotation – 缺省情况 @EnableAsync扫描Spring中@Async 注解和EJB 3.1 javax.ejb.Asynchronous注解; 该参数可以用于检查其他情况,如用户自定义注解。
mode – 指定使用通知类型 – JDK proxy-based 或 AspectJ weaving
proxyTargetClass – 指定使用的代理类型 – CGLIB 或 JDK; 该属性仅当 mode 被设置为AdviceMode.PROXY时有效。
order – 设置AsyncAnnotationBeanPostProcessor顺序; 缺省为最后一个,以便顾及所有已存在的代理。

异步处理也可以通过xml方式配置,使用task命名空间:

<task:executor id="myexecutor" pool-size="5"  /> 
<task:annotation-driven executor="myexecutor"/>

@Async

首先我们需要了解其规则——@Async有两个限制:

  • 必须仅应用在public方法上
  • 自执行——从相同类中调用异步方法不起作用。

原因很简单——public方法能够被代理,自执行不工作是因为它绕过代理并直接调用底层方法。

无返回值方法

下面简单配置是针对无返回值方法实现异步调用:

@Async 
public void asyncMethodWithVoidReturnType() { 
    System.out.println("Execute method asynchronously. " 
      + Thread.currentThread().getName()); 
}

带返回值方法

@Async也可以应用在带返回值方法——通过Future包装返回值类型:

@Async 
public Future<String> asyncMethodWithReturnType() { 
    System.out.println("Execute method asynchronously - " 
      + Thread.currentThread().getName()); 
    try { 
        Thread.sleep(5000); 
        return new AsyncResult<String>("hello world !!!!"); 
    } catch (InterruptedException e) { 
        // 
    } 
 
    return null; 
}

spring 也提供了实现Future接口的AsyncResult类,用于跟踪异步方法执行结果。下面示例是执行上面方法并返回Future类型的异步执行结果。

public void testAsyncAnnotationForMethodsWithReturnType() 
  throws InterruptedException, ExecutionException { 
    System.out.println("Invoking an asynchronous method. " 
      + Thread.currentThread().getName()); 
    Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType(); 
 
    while (true) { 
        if (future.isDone()) { 
            System.out.println("Result from asynchronous process - " + future.get()); 
            break; 
        } 
        System.out.println("Continue doing something else. "); 
        Thread.sleep(1000); 
    } 
}

Executor

Spring缺省使用SimpleAsyncTaskExecutor 实际以异步方式运行注解方法。缺省配置可以在两个级别上重新配置——应用级或每个方法级。

覆盖方法级Executor

相应Executor需要在配置类中声明:

@Configuration 
@EnableAsync 
public class SpringAsyncConfig {
    
 
    @Bean(name = "threadPoolTaskExecutor") 
    public Executor threadPoolTaskExecutor() { 
        return new ThreadPoolTaskExecutor(); 
    } 
}

然后在@Async注解中指定执行器名称:

@Async("threadPoolTaskExecutor") 
public void asyncMethodWithConfiguredExecutor() { 
    System.out.println("Execute method with configured executor - " 
      + Thread.currentThread().getName()); 
}

覆盖应用级Executor

配置类应该实现AsyncConfigurer 接口,并实现getAsyncExecutor() 方法,方法中返回整个应用使用的执行器,即所有@Async注解的方法缺省使用该执行器异步执行方法:

@Configuration 
@EnableAsync 
public class SpringAsyncConfig implements AsyncConfigurer {
    
 
    @Override 
    public Executor getAsyncExecutor() { 
        return new SimpleAsyncTaskExecutor(); 
    } 
 
}

异常处理

当方法返回Future类型,异常处理比较容易——Future.get()方法将抛出异常。但如果返回类型为void,异常将不传递至调用线程。因此我们需要增加额外配置去处理异常。

我们创建自定义异步异常处理器,通过实行AsyncUncaughtExceptionHandler接口。当有任何未捕获的异步异常则会执行handleUncaughtException方法:

public class CustomAsyncExceptionHandler 
  implements AsyncUncaughtExceptionHandler {
    
 
    @Override 
    public void handleUncaughtException( 
      Throwable throwable, Method method, Object... obj) { 
 
        System.out.println("Exception message - " + throwable.getMessage()); 
        System.out.println("Method name - " + method.getName()); 
        for (Object param : obj) { 
            System.out.println("Parameter value - " + param); 
        } 
    } 
 
}

在前节中,我们看到实现AsyncConfigurer 接口的配置类,下面增加异常部分配置。需要覆盖getAsyncUncaughtExceptionHandler() 方法,并返回我们自定义的异步异常处理器:

@Override 
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
    return new CustomAsyncExceptionHandler(); 
}

总结

本文我们讨论了spring中异步执行代码。首先了解基本配置和注解方法异步执行,也涉及高级配置——配置不同级别的执行器和异常处理策略。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/80919415