@Async 方法与 Spring @EnableAsync

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(342)   2023-06-26 07:54:18

学习在 @Async@EnableAsync 注释的帮助下在 Spring 框架中创建异步方法,这些注释在 Java $$$ 之上使用线程池 框架。

1. Spring @EnableAsync@Async

Spring 带有 @EnableAsync 注释,可以应用于 @Configuration 类以实现异步行为。 @EnableAsync 注释将查找标有 @Async 注释的方法并在后台线程池中运行它们。

@Async 注释方法在单独的线程中执行并返回 CompletableFuture 以保存异步计算的结果。

在 spring 中启用异步配置,请按照下列步骤操作:

  • 创建线程池以异步运行任务。
@Configuration
@EnableAsync
public class AsynchConfiguration 
{
  @Bean(name = "asyncExecutor")
  public Executor asyncExecutor() 
  {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(3);
    executor.setQueueCapacity(100);
    executor.setThreadNamePrefix("AsynchThread-");
    executor.initialize();
    return executor;
  }
}
  • @Async 注释该方法应该异步运行。该方法必须是 public 并且可能返回也可能不返回值。如果返回值返回值,则返回值应包装在 Future 接口实现中。
@Async("asyncExecutor")
public CompletableFuture<EmployeeNames> methodOne() throws InterruptedException {
  //code
}
  • 要组合多个异步任务的结果,请使用join() 方法。
CompletableFuture.allOf(methodOne, methodTwo, methodThree).join();

2. 带有异步任务的 Spring REST 控制器示例

在此演示中,我们将创建一个 REST API,它将异步地从三个远程服务获取数据,并在所有 3 个服务的响应都可用时聚合响应。

  • 调用EmployeeName API
  • 调用EmployeeAddress API
  • 调用EmployeePhone API
  • 等待上述服务的响应
  • 聚合所有三个 API 响应并构建最终响应以发送回客户端

2.1.要异步使用的远程 REST API

以下是异步 REST 控制器在聚合数据并返回结果之前必须使用的远程 API,

@RestController
public class EmployeeDataController {

  @RequestMapping(value = "/addresses", method = RequestMethod.GET)
  public EmployeeAddresses getAddresses() {
    //...
  }
 
  @RequestMapping(value = "/phones", method = RequestMethod.GET)
  public EmployeePhone getPhoneNumbers() {
    //...
  }
 
  @RequestMapping(value = "/names", method = RequestMethod.GET)
  public EmployeeNames getEmployeeName() {
    //...
  }
}

2.3. @Async 方法返回 CompletableFuture

这些服务方法将从远程 API 或数据库中提取数据,并且必须在单独的线程中并行运行以加快处理速度。

@Service
public class AsyncService {
 
  private static Logger log = LoggerFactory.getLogger(AsyncService.class);
 
  @Autowired
  private RestTemplate restTemplate;
 
  @Bean
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
 
  @Async
  public CompletableFuture<EmployeeNames> getEmployeeName() throws InterruptedException 
  {
    log.info("getEmployeeName starts");
 
    EmployeeNames employeeNameData = restTemplate.getForObject("http://localhost:8080/name", EmployeeNames.class);
 
    log.info("employeeNameData, {}", employeeNameData);
    Thread.sleep(1000L);  //Intentional delay
    log.info("employeeNameData completed");
    return CompletableFuture.completedFuture(employeeNameData);
  }
 
  @Async
  public CompletableFuture<EmployeeAddresses> getEmployeeAddress() throws InterruptedException 
  {
    log.info("getEmployeeAddress starts");
 
    EmployeeAddresses employeeAddressData = restTemplate.getForObject("http://localhost:8080/address", EmployeeAddresses.class);
 
    log.info("employeeAddressData, {}", employeeAddressData);
    Thread.sleep(1000L);  //Intentional delay
    log.info("employeeAddressData completed");
    return CompletableFuture.completedFuture(employeeAddressData);
  }
 
  @Async
  public CompletableFuture<EmployeePhone> getEmployeePhone() throws InterruptedException 
  {
    log.info("getEmployeePhone starts");
 
    EmployeePhone employeePhoneData = restTemplate.getForObject("http://localhost:8080/phone", EmployeePhone.class);
 
    log.info("employeePhoneData, {}", employeePhoneData);
    Thread.sleep(1000L);  //Intentional delay
    log.info("employeePhoneData completed");
    return CompletableFuture.completedFuture(employeePhoneData);
  }
}

2.3.调用异步方法和聚合结果

这是调用异步方法、使用和聚合它们的响应并返回给客户端的主要 API。

@RestController
public class AsyncController {
 
  private static Logger log = LoggerFactory.getLogger(AsyncController.class);
 
  @Autowired
  private AsyncService service;
 
  @RequestMapping(value = "/testAsynch", method = RequestMethod.GET)
  public void testAsynch() throws InterruptedException, ExecutionException 
  {
    log.info("testAsynch Start");
 
    CompletableFuture<EmployeeAddresses> employeeAddress = service.getEmployeeAddress();
    CompletableFuture<EmployeeNames> employeeName = service.getEmployeeName();
    CompletableFuture<EmployeePhone> employeePhone = service.getEmployeePhone();
 
    // Wait until they are all done
    CompletableFuture.allOf(employeeAddress, employeeName, employeePhone).join();
     
    log.info("EmployeeAddress--> " + employeeAddress.get());
    log.info("EmployeeName--> " + employeeName.get());
    log.info("EmployeePhone--> " + employeePhone.get());
  }
}

3. 异常处理

当方法返回类型是 Future 时,Future.get() 方法将抛出异常,我们应该使用 try-catch 块来在聚合结果之前捕获并处理异常。

问题是如果异步方法不返回任何值,那么很难知道在方法执行时是否发生了异常。我们可以使用 AsyncUncaughtExceptionHandler 实现来捕获和处理此类异常。

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }
}
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

  private final Logger logger = LoggerFactory.getLogger(AsyncExceptionHandler.class);

  @Override
  public void handleUncaughtException(Throwable ex, Method method, Object... params) {
      logger.error("Unexpected asynchronous exception at : "
              + method.getDeclaringClass().getName() + "." + method.getName(), ex);
  }
}

4.运行演示

下载并启动这两个应用程序。点击 API:http://localhost:8081/testAsynch。观察控制台中的输出。

使用@Async

With Aync Enabled

没有@Async

Without Aync Methods Enabled

源代码下载链接1链接2

将有关创建 spring boot 非阻塞 rest api 的问题告诉我。

快乐学习!!

地址:https://www.cundage.com/article/enableasync-async-controller.html

相关阅读

学习在 @Async 和 @EnableAsync 注释的帮助下在 Spring 框架中创建异步方法,这些注释在 Java $$$ 之上使用线程池 框架。 1. Spring @EnableAs...
学习编写 spring boot async rest controller,它支持异步请求处理并使用 Callable 接口返回响应。 1. spring boot异步控制器 编写控制器并让它...
学习使用 ResponseBodyEmitter 编写 spring boot async rest controller。当我们有一个服务,或者多个调用,并且想要收集结果并将响应发送给客户端时...
学习使用 Spring Boot 和 Jersey 框架配置和创建 JAX-RS 2.0 REST API。此示例应用程序使用 Jersey 的 ServletContainer 来部署 RES...
学习Spring Boot有以下深度教程,涵盖从基本概念到打包、部署、监控等高级概念。 1. 开始 Spring Boot 简介 spring-boot-starter-parent 示例 sp...
学习使用 Spring Boot 和 Jersey 框架创建 JAX-RS 2.0 REST API,并添加 &lt; strong&gtl;基于角色的安全性 使用 JAX-RS 注释,例如@P...
在 的帮助下学习在 Spring boot 2 应用程序中使用 Junit 5 编写单元测试RestTemplate,用于测试 REST API 或 spring mvc 应用程序。 1.Mav...
在此 spring boot 异常处理程序 教程中,我们将学习验证请求正文 发送到 PUT/POST REST API。我们还将学习在 API 响应中为验证错误 添加自定义错误消息。 在这个 s...
在此Spring Boot RestTemplate获取请求示例中,学习使用休消息模板调用HTTP GET API并验证响应状态码和响应实体主体。 要创建其余 API,请使用 spring bo...
在 Spring REST JSON 示例中,我们将学习创建能够返回 JSON 表示的REST API资源。我们将使用以下方法来配置 JSON 响应: @ResponseBody注释 Mappi...