springBoot @async详解


1、SpringBoot异步处理支持

Spring提供了@Aysnc注解进行便捷的异步处理支持,只需要在bean的方法上标注@Async注解即可使得方法异步提交到线程池处理,调用方无需等待。

2、如何使用?

在SpringBoot中使用只需使用Java代码配置或者XML配置:

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

@EnableAsync有值得注意的属性:

  • annotation – 默认情况下, @EnableAsync 只检测 Spring的 @Async 注解 和 EJB 3.1的 javax.ejb.Asynchronous注解;这个属性可以配置额外的需要检测的注解,所以我们可以自定义注解。
  • mode – 指定异步代理类生成模式 – JDK 或者 AspectJ 织入
  • proxyTargetClass – 指定使用的代理模式 -CGLIB 或者 JDK; 这个属性只有在将上述 mode 属性设置为 AdviceMode.PROXY才有效
  • order – 设置 AsyncAnnotationBeanPostProcessor后置处理器的调用优先级,默认情况下,这个后置处理器是最后执行,确保能代理所有bean

3、@Async注解

@Async有2个限制使用条件:

  • 必须使用在public方法上,因为私有的方法无法被代理
  • 自我调用 – 在同一个类中调用标注@Async的方法。因为在代理类中调用会直接调用原对象的方法

3.1、方法返回void

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

3.2、方法具有返回值

如果方法有返回值,则需要用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提供了一个AsyncResult类,它实现了JDK的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);
    }
}

4、配置线程池

默认,Spring使用SimpleAsyncTaskExecutor来执行异步任务,但是可以在应用级和方法级进行自定义线程池:

4.1、方法级自定义线程池

首先配置一个自定义的线程池bean:

@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());
}

4.2、应用级自定义线程池

给整个应用配置默认的线程池需要实现AsyncConfigurer接口,需要实现其getAsyncExecutor()方法,这个方法放回的线程池作为整个应用的异步任务线程池

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }

}

5、异常处理

如果一步方法返回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()方法来注册这个异常处理器:

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }

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

}

文章作者: Ubi-potato
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ubi-potato !
评论
  目录