Spring Boot 中的异步请求和异步调用详解

开发 前端
在现代应用程序中,异步处理可以显著提升性能和响应速度。除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。

在现代应用程序中,异步处理可以显著提升性能和响应速度。除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。这个时候正常就是启一个新线程去做一些业务处理,让主线程异步的执行其他业务。本文将详细介绍如何在 Spring Boot 中实现异步请求和异步调用,帮助大家在实际项目中应用这些技术。

一、异步请求

1.1 使用 @Async 注解

Spring 提供了 @Async注解,使得我们可以简单地将一个方法变成异步方法。为了启用异步支持,需要在配置类上添加 @EnableAsync注解。

步骤:

1. 添加依赖:

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter</artifactId>
   </dependency>

2. 启用异步支持:

import org.springframework.context.annotation.Configuration;
   import org.springframework.scheduling.annotation.EnableAsync;
   @Configuration
   @EnableAsync
   public class AsyncConfig {
   }

3. 创建异步方法:

import org.springframework.scheduling.annotation.Async;
   import org.springframework.stereotype.Service;
   @Service
   public class AsyncService {
       @Async
       public void asyncMethod() {
           System.out.println("异步方法开始: " + Thread.currentThread().getName());
           try {
               Thread.sleep(2000); // 模拟长时间任务
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("异步方法结束: " + Thread.currentThread().getName());
       }
   }

4. 调用异步方法:

import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.web.bind.annotation.GetMapping;
   import org.springframework.web.bind.annotation.RestController;
   @RestController
   public class AsyncController {
       @Autowired
       private AsyncService asyncService;
       @GetMapping("/async")
       public String callAsync() {
           asyncService.asyncMethod();
           return "请求已提交";
       }
   }

1.2 使用 CompletableFuture

CompletableFuture 是 Java 8 引入的一个强大的异步工具类,Spring 也对其提供了良好的支持。

示例:

1. 创建返回 CompletableFuture 的异步方法:

import org.springframework.scheduling.annotation.Async;
   import org.springframework.stereotype.Service;
   import java.util.concurrent.CompletableFuture;
   @Service
   public class AsyncService {
       @Async
       public CompletableFuture<String> asyncMethodWithResult() {
           System.out.println("异步方法开始: " + Thread.currentThread().getName());
           try {
               Thread.sleep(2000); // 模拟长时间任务
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("异步方法结束: " + Thread.currentThread().getName());
           return CompletableFuture.completedFuture("异步任务完成");
       }
   }

2. 调用异步方法并获取结果:

import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.web.bind.annotation.GetMapping;
   import org.springframework.web.bind.annotation.RestController;
   import java.util.concurrent.CompletableFuture;
   import java.util.concurrent.ExecutionException;
   @RestController
   public class AsyncController {
       @Autowired
       private AsyncService asyncService;
       @GetMapping("/asyncResult")
       public String callAsyncWithResult() throws ExecutionException, InterruptedException {
           CompletableFuture<String> result = asyncService.asyncMethodWithResult();
           return result.get();
       }
   }

二、异步调用

异步调用不仅可以应用在请求中,也可以在方法之间的调用中使用。

2.1 使用 @Async 进行方法调用

可以直接在类的方法上使用 @Async 注解,使其成为异步方法。

示例:

1. 创建异步调用方法:

import org.springframework.scheduling.annotation.Async;
   import org.springframework.stereotype.Component;
   @Component
   public class AsyncComponent {
       @Async
       public void asyncMethod() {
           System.out.println("异步方法开始: " + Thread.currentThread().getName());
           try {
               Thread.sleep(2000); // 模拟长时间任务
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("异步方法结束: " + Thread.currentThread().getName());
       }
   }

2. 在其他类中调用异步方法:

import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.stereotype.Service;
   @Service
   public class SomeService {
       @Autowired
       private AsyncComponent asyncComponent;
       public void performAsyncTask() {
           System.out.println("主线程开始: " + Thread.currentThread().getName());
           asyncComponent.asyncMethod();
           System.out.println("主线程结束: " + Thread.currentThread().getName());
       }
   }

3. 在控制器中调用服务方法: 

import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.web.bind.annotation.GetMapping;
   import org.springframework.web.bind.annotation.RestController;
   @RestController
   public class SomeController {
       @Autowired
       private SomeService someService;
       @GetMapping("/performTask")
       public String performTask() {
           someService.performAsyncTask();
           return "任务已提交";
       }
   }

三、常见问题和注意事项

1.异步和同步请求的主要区别

两者的使用场景不同,异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量;而异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析等。

异步请求是会一直等待response相应的,需要返回结果给客户端的;而异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。

2.使用方式(基于spring下)

  • 需要在启动类加入@EnableAsync使异步调用@Async注解生效
  • 在需要异步执行的方法上加入此注解即可@Async("threadPool"),threadPool为自定义线程池
  • 代码略。。。就俩标签,自己试一把就可以了

3.注意事项

  • 在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。所以最好我们来自定义一个线程池。
  • 调用的异步方法,不能为同一个类的方法(包括同一个类的内部类),简单来说,因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。其他的注解如@Cache等也是一样的道理,说白了,就是Spring的代理机制造成的。所以在开发中,最好把异步服务单独抽出一个类来管理。下面会重点讲述。。

4.什么情况下会导致@Async异步方法会失效?

  1. 调用同一个类下注有@Async异步方法:在spring中像@Async和@Transactional、cache等注解本质使用的是动态代理,其实Spring容器在初始化的时候Spring容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器,那么解决方法也会沿着这个思路来解决。
  2. 调用的是静态(static )方法
  3. 调用(private)私有化方法

5.解决4中问题a的方式(其它b,c两个问题自己注意下就可以了)

  1. 将要异步执行的方法单独抽取成一个类,原理就是当你把执行异步的方法单独抽取成一个类的时候,这个类肯定是被Spring管理的,其他Spring组件需要调用的时候肯定会注入进去,这时候实际上注入进去的就是代理类了。
  2. 其实我们的注入对象都是从Spring容器中给当前Spring组件进行成员变量的赋值,由于某些类使用了AOP注解,那么实际上在Spring容器中实际存在的是它的代理对象。那么我们就可以通过上下文获取自己的代理对象调用异步方法。

四、总结

在 Spring Boot 中实现异步请求和异步调用可以显著提升应用程序的性能和响应速度。通过使用 @Async注解和 CompletableFuture,我们可以轻松地将同步方法转换为异步方法。本文介绍了具体的实现步骤和示例,希望能帮助你在项目中更好地应用异步处理技术。

责任编辑:华轩 来源: 微技术之家
相关推荐

2024-10-15 10:28:43

2020-01-02 16:30:02

Spring BootJava异步请求

2021-03-29 09:26:44

SpringBoot异步调用@Async

2024-08-12 10:13:01

2022-09-27 12:01:56

Spring异步调用方式

2010-01-11 17:24:19

VB.NET异步调用

2022-09-28 14:54:07

Spring注解方式线程池

2021-02-17 09:09:15

异步请求

2018-06-21 14:46:03

Spring Boot异步调用

2009-10-20 16:48:30

C#委托

2009-12-21 14:10:26

WCF异步调用

2009-11-09 10:50:30

WCF异步调用

2009-07-01 14:23:46

JavaScript异

2009-07-01 14:37:14

JavaScript异

2009-07-01 13:58:00

JavaScript异

2009-11-06 15:54:15

WCF异步调用

2009-07-01 14:31:01

JavaScript异

2009-08-21 13:18:25

C#异步调用

2009-08-21 11:02:55

C#异步调用

2022-07-01 08:14:28

Dubbo异步代码
点赞
收藏

51CTO技术栈公众号