SpringBoot - 网络请求客户端WebClient使用详解

article/2025/9/12 6:21:29

在 Spring 5 之前,如果我们想要调用其他系统提供的 HTTP 服务,通常可以使用 Spring 提供的 RestTemplate 来访问,不过由于 RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客户端,因此存在一定性能瓶颈。根据 Spring 官方文档介绍,在将来的版本中它可能会被弃用。

​ 作为替代,Spring 官方已在 Spring 5 中引入了 WebClient 作为非阻塞式 Reactive HTTP 客户端。下面通过样例演示如何使用 WebClient。

一、基本介绍

1,什么是 WebClient

  • 从 Spring 5 开始,Spring 中全面引入了 Reactive 响应式编程。而 WebClient 则是 Spring WebFlux 模块提供的一个非阻塞的基于响应式编程的进行 Http 请求的客户端工具。
  • 由于 WebClient 的请求模式属于异步非阻塞,能够以少量固定的线程处理高并发的 HTTP 请求。因此,从 Spring 5 开始,HTTP 服务之间的通信我们就可以考虑使用 WebClient 来取代之前的 RestTemplate。

2,WebClient 的优势

(1)与 RestTemplate 相比,WebClient 有如下优势:

  • 非阻塞,Reactive 的,并支持更高的并发性和更少的硬件资源。
  • 提供利用 Java 8 lambdas 的函数 API。
  • 支持同步和异步方案。
  • 支持从服务器向上或向下流式传输。

(2)RestTemplate 不适合在非阻塞应用程序中使用,因此 Spring WebFlux 应用程序应始终使用 WebClient。在大多数高并发场景中,WebClient 也应该是 Spring MVC 中的首选,并且用于编写一系列远程,相互依赖的调用。

3,安装配置

编辑 pom.xml 文件,添加 Spring WebFlux 依赖,从而可以使用 WebClient。

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

二、创建 WebClient 实例

​ 从 WebClient 的源码中可以看出,WebClient 接口提供了三个不同的静态方法来创建 WebClient 实例:

1,利用 create() 创建

(1)下面利用 create() 方法创建一个 WebClient 对象,并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于利用 create() 创建的 WebClient 对象没有设定 baseURL,所以这里的 uri() 方法相当于重写 baseURL。

WebClient webClient = WebClient.create();Mono<String> mono = webClient.get() // GET 请求.uri("http://jsonplaceholder.typicode.com/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换System.out.println(mono.block());

2,利用 create(String baseUrl) 创建
(1)下面利用 create(String baseUrl) 方法创建一个 WebClient 对象,并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于利用 create(String baseUrl) 创建的 WebClient 对象时已经设定了 baseURL,所以 uri() 方法会将返回的结果和 baseUrl 进行拼接组成最终需要远程请求的资源 URL。
WebClient webClient = WebClient.create("http://jsonplaceholder.typicode.com");Mono<String> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换System.out.println(mono.block());

3,利用 builder 创建(推荐)

(1)下面使用 builder() 返回一个 WebClient.Builder,然后再调用 build 就可以返回 WebClient 对象。并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于返回的不是 WebClient 类型而是 WebClient.Builder,我们可以通过返回的 WebClient.Builder 设置一些配置参数(例如:baseUrl、header、cookie 等),然后再调用 build 就可以返回 WebClient 对象了
WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").defaultHeader(HttpHeaders.USER_AGENT,"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)").defaultCookie("ACCESS_TOKEN", "test_token").build();Mono<String> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换System.out.println(mono.block());

三、GET 请求

1,获取 String 结果数据

下面代码将响应结果映射为一个 String 字符串,并打印出来。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {Mono<String> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换System.out.println(mono.block());return;}
}

2,将结果转换为对象

(1)当响应的结果是 JSON 时,也可以直接指定为一个 Object,WebClient 将接收到响应后把 JSON 字符串转换为对应的对象。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {Mono<PostBean> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(PostBean.class); //响应数据类型转换System.out.println(mono.block());return;}
}

(2)其中定义的实体 Bean 代码如下:

@Getter
@Setter
@ToString
public class PostBean {private int userId;private int id;private String title;private String body;
}

3,将结果转成集合

(1)假设接口返回的是一个 json 数组,内容如下:

在这里插入图片描述

(2)我们也可以将其转成对应的 Bean 集合:

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {Flux<PostBean> flux = webClient.get() // GET 请求.uri("/posts")  // 请求路径.retrieve() // 获取响应体.bodyToFlux(PostBean.class); //响应数据类型转换List<PostBean> posts = flux.collectList().block();System.out.println("结果数:" + posts.size());return;}
}

4,参数传递的几种方式

下面 3 种方式的结果都是一样的。

(1)使用占位符的形式传递参数:

Mono<String> mono = webClient.get() // GET 请求.uri("/{1}/{2}", "posts", "1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换

(2)另一种使用占位符的形式:

String type = "posts";
int id = 1;Mono<String> mono = webClient.get() // GET 请求.uri("/{type}/{id}", type, id)  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换System.out.println(mono.block());

(3)我们也可以使用 map 装载参数:

Map<String,Object> map = new HashMap<>();
map.put("type", "posts");
map.put("id", 1);Mono<String> mono = webClient.get() // GET 请求.uri("/{type}/{id}", map)  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换

5,subscribe 订阅(非阻塞式调用)

(1)前面的样例我们都是人为地使用 block 方法来阻塞当前程序。其实 WebClient 是异步的,也就是说等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {System.out.println("--- begin ---");Mono<String> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换// 订阅(异步处理结果)mono.subscribe(result -> {System.out.println(result);});System.out.println("--- end ---");return;}
}

附:使用 exchange() 方法获取完整的响应内容

1,方法介绍
(1)前面我们都是使用 retrieve() 方法直接获取到了响应的内容,如果我们想获取到响应的头信息、Cookie 等,可以在通过 WebClient 请求时把调用 retrieve() 改为调用 exchange()。
(2)通过 exchange() 方法可以访问到代表响应结果的对象,通过该对象我们可以获取响应码、contentType、contentLength、响应消息体等。

2,使用样例

下面代码请求一个网络接口,并将响应体、响应头、响应码打印出来。其中响应体的类型设置为 String。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {Mono<ClientResponse> mono = webClient.get() // GET 请求.uri("/posts/1")  // 请求路径.exchange();// 获取完整的响应对象ClientResponse response = mono.block();HttpStatus statusCode = response.statusCode(); // 获取响应码int statusCodeValue = response.rawStatusCode(); // 获取响应码值Headers headers = response.headers(); // 获取响应头// 获取响应体Mono<String> resultMono = response.bodyToMono(String.class);String body = resultMono.block();// 输出结果System.out.println("statusCode:" + statusCode);System.out.println("statusCodeValue:" + statusCodeValue);System.out.println("headers:" + headers.asHttpHeaders());System.out.println("body:" + body);return;}
}

四、POST 请求

1,发送一个 JSON 格式数据(使用 json 字符串)

(1)下面代码使用 post 方式发送一个 json 格式的字符串,并将结果打印出来(以字符串的形式)。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {// 需要提交的 json 字符串String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";// 发送请求Mono<String> mono = webClient.post() // POST 请求.uri("/posts")  // 请求路径.contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(jsonStr)).retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换// 输出结果System.out.println(mono.block());return;}
}

2,发送一个 JSON 格式数据(使用 Java Bean)

(1)下面代码使用 post 方式发送一个 Bean 对象,并将结果打印出来(以字符串的形式)。结果同上面是一样的:

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {// 要发送的数据对象PostBean postBean = new PostBean();postBean.setUserId(222);postBean.setTitle("abc");postBean.setBody("航歌");// 发送请求Mono<String> mono = webClient.post() // POST 请求.uri("/posts")  // 请求路径.contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(postBean).retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换// 输出结果System.out.println(mono.block());return;}
}

(2)上面发送的 Bean 对象实际上会转成如下格式的 JSON 数据提交:

3,使用 Form 表单的形式提交数据

(1)下面样例使用 POST 方式发送 multipart/form-data 格式的数据:

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {//提交参数设置MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("title", "abc");map.add("body", "航歌");// 发送请求Mono<String> mono = webClient.post() // POST 请求.uri("/posts")  // 请求路径.contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData(map)).retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换// 输出结果System.out.println(mono.block());return;}
}

(2)上面代码最终会通过如下这种 form 表单方式提交数据:

4,将结果转成自定义对象
​ 上面样例我们都是将响应结果以 String 形式接收,其实 WebClient 还可以自动将响应结果转成自定的对象或则数组。具体可以参考前面写的文章:

5,设置 url 参数

(1)如果 url 地址上面需要传递一些参数,可以使用占位符的方式:

String url = "http://jsonplaceholder.typicode.com/{1}/{2}";
String url = "http://jsonplaceholder.typicode.com/{type}/{id}";

(2)具体的用法可以参考前面写的文章:

6,subscribe 订阅(非阻塞式调用)

(1)前面的样例我们都是人为地使用 block 方法来阻塞当前程序。其实 WebClient 是异步的,也就是说等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {System.out.println("--- begin ---");// 需要提交的 json 字符串String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";Mono<String> mono = webClient.post() // POST 请求.uri("/posts")  // 请求路径.contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(jsonStr)).retrieve() // 获取响应体.bodyToMono(String.class); //响应数据类型转换// 订阅(异步处理结果)mono.subscribe(result -> {System.out.println(result);});System.out.println("--- end ---");return;}
}

附:使用 exchange() 方法获取完整的响应内容

1,方法介绍
(1)前面我们都是使用 retrieve() 方法直接获取到了响应的内容,如果我们想获取到响应的头信息、Cookie 等,可以在通过 WebClient 请求时把调用 retrieve() 改为调用 exchange()。
(2)通过 exchange() 方法可以访问到代表响应结果的对象,通过该对象我们可以获取响应码、contentType、contentLength、响应消息体等。

2,使用样例
下面代码请求一个网络接口,并将响应体、响应头、响应码打印出来。其中响应体的类型设置为 String。

@RestController
public class HelloController {// 创建 WebClient 对象private WebClient webClient = WebClient.builder().baseUrl("http://jsonplaceholder.typicode.com").build();@GetMapping("/test")public void test() {// 需要提交的 json 字符串String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";// 发送请求Mono<ClientResponse> mono = webClient.post() // POST 请求.uri("/posts")  // 请求路径.contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(jsonStr)).exchange();// 获取完整的响应对象ClientResponse response = mono.block();HttpStatus statusCode = response.statusCode(); // 获取响应码int statusCodeValue = response.rawStatusCode(); // 获取响应码值Headers headers = response.headers(); // 获取响应头// 获取响应体Mono<String> resultMono = response.bodyToMono(String.class);String body = resultMono.block();// 输出结果System.out.println("statusCode:" + statusCode);System.out.println("statusCodeValue:" + statusCodeValue);System.out.println("headers:" + headers.asHttpHeaders());System.out.println("body:" + body);return;}
}

http://chatgpt.dhexx.cn/article/bdkeJiHS.shtml

相关文章

【QString 函数学习篇】

【QString 函数学习篇】 【1】UI设计布局【2】QChar | setAlignment |【3】sprintf | asprintf | setNum | number |【4】toInt | toUpper [十进制->十六进制 | 十进制->二进制]【5】clear | append【6】二进制->十六进制 | 二进制->十进制【7】prepend【8】strimm…

Qt扫盲-QString使用总结

QString使用总结 一、概述二、初始化字符串1、极速版2、原理版 三、操作字符串1、极速版1. 增加2. 删除3. 修改4. 插入5. 转换 2、原理版 四、查询字符串五、字符串格式转换六、Null 字符串和 Empty 字符串的区别七、字符串参数格式化八、更高效的字符串构造九、最大大小和出现…

Qt之QString字符串类的详细介绍及功能函数示例用法

前序&#xff1a;为了加深对Qt各个类的认识和了解&#xff0c;决定以后再闲暇的时间对Qt类进行详细的阅读&#xff08;基于Qt cteater 的帮助文档&#xff09;&#xff0c;这样既可以提高自己的英文阅读能力&#xff0c;也可以对各个类有一个更加深入的认识和了解。 注&#xf…

QString类详解

文章目录 一、简要介绍1.1 隐式共享 二、常用方法2.1 常用构造函数2.2 字符串与数值之间的转换2.3 添加字符串2.4 大小写转换2.5 长度2.6 其他常用函数 一、简要介绍 在Qt中表示字符串的类是QString类&#xff0c;它存储字符串是采用的Unicode码&#xff0c;编码方式是使用UTF…

QString(详解)

目录 一、QString转char* 二、合并字符串 三、分割字符串 split()函数 mid(pos,n)函数 QString::section truncate(int)函数&#xff0c;从指定位置截断字符串str 四、去除字符串两端的空白 QString::trimmed()函数 QString::simplified()函数 五、查找是否包含字符…

Java——JLabel自动换行问题

┏(ω)☞ 本专栏的目录&#xff08;为您提供更好的查询方式&#xff09;&#xff08;点这里说不定有你想要的&#xff09; JLabel类可显示文本&#xff0c;一般直接添加文字会导致如下问题&#xff08;字体显示不全&#xff09;&#xff0c;在不同过增加文本框的长度的情况下可…

JLabel展示文本和图片

JLabel 构造方法 JLabel&#xff08;&#xff09;创建一个不含图像&#xff0c;不含文本的 JLabel 对象 JLabel&#xff08;String text&#xff09;使用指定的文本创建一个 JLabel 对象 JLabel&#xff08;Icon image&#xff09;创建一个具有指定图像的 JLabel 对象 pack…

java设置JLabel字体字号颜色

JLabel jl; Font f new Font("宋体",Font.PLAIN,16); jl.setFont(f); jl.setForeground(Color.red); 效果图&#xff1a;

JLabel设置背景颜色和设置文字颜色

JLabel设置背景颜色&#xff1a; JLabel light1 new JLabel("wuli"); light1.setOpaque(true); //此句是重点&#xff0c;设置背景颜色必须先将它设置为不透明的&#xff0c;因为默认是透明的。。。 light1.setBackground(Color.GREEN); JLabel设置文字颜色&…

【Java|Swing】关于JLabel换行

最近在用Swing做Windows的窗口程序&#xff0c;在使用JLabel时发现使用\n没办法换行&#xff0c;于是乎借助百度一下弄懂了JLabel是如何实现换行的。 先可以看一下使用\n的效果 JLabel labelnew JLabel("数据版本\nv1.0.0"); 运行测试 很明显&#xff0c;换行失败了…

初识JFrame、JPanel、JLabel

JFrame&#xff1a;java图形化界面设计&#xff0c;是一个顶层的框架类&#xff0c;相当于容器。好比一个窗户的框子&#xff0c;这个框子可以嵌入几个玻璃窗。 JPanel&#xff1a;也是一个容器类&#xff0c;相当于一个大玻璃窗。 JLabel&#xff1a;是一些基础组件&#xff0…

JFrame,JPanel,JLabel详解

JFrame是一个顶层的框架类&#xff0c;好比一个窗户的框子。也是一个容器类。这个框子可以嵌入几个玻璃窗。 JPanel是一个容器类&#xff0c;相当于一大玻璃窗。 JLabel等是一些基础组件&#xff0c;它必须置于某个容器里&#xff0c;类似于窗花、剪纸&#xff0c;必须置于窗户…

Java图形界面之JFrame,JLabel,JButton

Java图形界面之JFrame&#xff0c;JLabel&#xff0c;JButton 前言 ​ Java第一卷​ 图形界面第一节 JFrame package Demo03;import javax.swing.*; import java.awt.*;public class JFrame01 {public static void main(String[] args) {JFrame f new JFrame("第一个Swi…

Java GUI编程4---标签组件JLabel

1. 认识JLabel JLabel组件表示的是一个标签&#xff0c;本身是用于显示信息的&#xff0c;一般情况下是不能直接更改器显示内容的。创建完的Label对象可以通过Container类中的add()方法&#xff0c;加入到容器中&#xff0c;JLabel类的常用方法和常量如下表所示。 序号常量描…

JLabel标签较为常见部分的使用方法

本篇文章将会教会大家如何使用JLabel 1添加JLabel //创建一个标签 JLabel jLabel new JLabel("Hi,我是标签"); //添加到面板 Jpanel1.add(jLabel); //将面板添加到窗口 jFrame.add(Jpanel1);2设置文本颜色 //设置文本颜色 jLabel.setForeground(Color.green);3设…

Java基础-JLable组件、文本框组件、JPanel轻量级容器、Swing事件处理三种方式、适配器

1、JLable标签组件 标签是一种可以包含文本和图片的非交互组件&#xff0c;其文本可以是单行文本&#xff0c;也可以是 HTML 文本。对于只包含文本的标签可以使用 JLabel 类&#xff0c;该类的主要构造方法有如下几种形式。 JLabel()&#xff1a;创建无图像并且标题为空字符串…

06.JavaSwing(基础组件——JLabel类)

软件界面是软件和用户之间的交流平台&#xff0c;而组件则是绘制软件界面的基本元素&#xff0c;是软件和用户之间的交流要素。 一、概述 JLabel组件用来显示文本和图像&#xff0c;可以只显示其一种&#xff0c;也可以同时显示。JLabel类提供了一些列用来设置标签的方法。 二…

从DTW到DILATE---时序预测损失函数总结

时间序列预测损失函数 时间序列预测是指通过分析历史数据来预测未来数据的变化趋势。时间序列预测在许多领域都有着广泛的应用&#xff0c;例如金融、气象、交通等。为了能够准确预测未来数据&#xff0c;时间序列预测中损失函数的选择非常关键。损失函数的选择直接影响模型的…

DTW算法——matlab实现(二)

DTW算法——Matlab实现 本文只是简单的介绍DTW算法的目的和实现。具体的DTW可以参考一下文献&#xff1a; 离散序列的一致性度量方法&#xff1a;动态时间规整&#xff08;DTW&#xff09; http://blog.csdn.net/liyuefeilong/article/details/45748399 动态时间归整/规整/弯曲…

【时间序列】DTW算法详解

作者 | 追光者 研究 | 机器学习与时间序列 出品 | AI蜗牛车 1.DTW 1.1 时序相似度 在时间序列数据中&#xff0c;一个常见的任务是比较两个序列的相似度&#xff0c;作为分类或聚类任务的基础。那么&#xff0c;时间序列的相似度应该如何计算呢&#xff1f; “ 经典的时间序列相…