弃用 RestTemplate,来了解一下官方推荐的 WebClient !
在 Spring Framework 5.0 及更高版本中,RestTemplate 已被弃用,取而代之的是较新的 WebClient。这意味着虽然 RestTemplate 仍然可用,但鼓励 Spring 开发人员迁移到新项目的 WebClient。
WebClient 优于 RestTemplate 的原因有几个:
-
非阻塞 I/O:WebClient 构建在 Reactor 之上,它提供了一种非阻塞、反应式的方法来处理 I/O。这可以在高流量应用程序中实现更好的可扩展性和更高的性能。
-
函数式风格:WebClient 使用函数式编程风格,可以使代码更易于阅读和理解。它还提供了流畅的 API,可以更轻松地配置和自定义请求。
-
更好地支持流式传输:WebClient 支持请求和响应正文的流式传输,这对于处理大文件或实时数据非常有用。
-
改进的错误处理:WebClient 提供比 RestTemplate 更好的错误处理和日志记录,从而更轻松地诊断和解决问题。
重点:即使升级了spring web 6.0.0版本,也无法在HttpRequestFactory中设置请求超时,这是放弃使用 RestTemplate 的最大因素之一。
设置请求超时不会有任何影响
总的来说,虽然 RestTemplate 可能仍然适用于某些用例,但 WebClient 提供了几个优势,使其成为现代 Spring 应用程序的更好选择。
让我们看看如何在 SpringBoot 3 应用程序中使用 WebClient。
(1) 创建网络客户端:
import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.TimeoutException;
import jakarta.annotation.PostConstruct;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;HttpClient httpClient =HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout).responseTimeout(Duration.ofMillis(requestTimeout)).doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(readTimeout)));WebClient client =WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
(2) 同步发送请求(就像RestTemplate一样)
如果你想坚持使用发送 HTTP 请求并等待响应的老方法,也可以使用 WebClient 实现如下所示的相同功能:
public String postSynchronously(String url, String requestBody) {LOG.info("Going to hit API - URL {} Body {}", url, requestBody);String response = "";try {response =client.method(HttpMethod.POST).uri(url).accept(MediaType.ALL).contentType(MediaType.APPLICATION_JSON).bodyValue(requestBody).retrieve().bodyToMono(String.class).block();} catch (Exception ex) {LOG.error("Error while calling API ", ex);throw new RunTimeException("XYZ service api error: " + ex.getMessage());} finally {LOG.info("API Response {}", response);}return response;
}
block()用于同步等待响应,这可能并不适合所有情况,你可能需要考虑subscribe()异步使用和处理响应。
(3) 异步发送请求:
有时我们不想等待响应,而是希望异步处理响应,这可以按如下方式完成:
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;public static Mono<String> makePostRequestAsync(String url, String postData) {WebClient webClient = WebClient.builder().build();return webClient.post().uri(url).contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData("data", postData)).retrieve().bodyToMono(String.class);
}
要使用此函数,只需传入要向其发送 POST 请求的 URL 以及要在请求正文中以 URL 编码字符串形式发送的数据。该函数将返回来自服务器的响应,或者如果请求由于任何原因失败,则返回一条错误消息。
请注意,在此示例中,WebClient是使用默认配置构建的。你可能需要根据不同要求进行不同的配置。
另请注意,block()用于同步等待响应,这可能并不适合所有情况。你可能需要考虑subscribe()异步使用和处理响应。
要使用响应,您可以订阅Mono并异步处理响应。下面是一个例子:
makePostRequestAsync( "https://example.com/api" , "param1=value1¶m2=value2" )
.subscribe(response -> { // 处理响应System.out.println ( response );
}, error -> { / / 处理错误System.err.println ( error .getMessage ()); }
);
subscribe()用于异步处理响应,你可以提供两个 lambda 表达式作为 subscribe() 的参数。如果请求成功并收到响应作为参数,则执行第一个 lambda 表达式;如果请求失败并收到错误作为参数,则执行第二个 lambda 表达式。
(4) 处理4XX和5XX错误:
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;public static Mono<String> makePostRequestAsync(String url, String postData) {WebClient webClient = WebClient.builder().baseUrl(url).build();return webClient.post().uri("/").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData("data", postData)).retrieve().onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Client error"))).onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Server error"))).bodyToMono(String.class);
}
在此示例中,该onStatus()方法被调用两次,一次针对 4xx 客户端错误,一次针对 5xx 服务器错误。onStatus() 每次调用都采用两个参数:
-
aPredicate确定错误状态代码是否与条件匹配
-
aFunction用于返回Mono,即要传播到订阅者的错误信息。
如果状态代码与条件匹配,Mono则会发出相应的状态代码,并且Mono链会因错误而终止。在此示例中,Mono 将发出一条 RuntimeException 错误消息,指示该错误是客户端错误还是服务器错误。
(5) 根据错误状态采取行动:
要根据Mono的subscribe()方法中的错误采取操作,可以在subscribe函数中处理响应的lambda表达式之后添加另一个lambda表达。如果在处理Monumber的过程中出现错误,则执行第二个lambda表达式。
下面是如何使用makePostRequestAsync函数和处理subscribe方法中的错误的更新示例:
makePostRequestAsync("https://example.com/api", "param1=value1¶m2=value2")
.subscribe(response -> {// handle the responseSystem.out.println(response);
}, error -> {// handle the errorSystem.err.println("An error occurred: " + error.getMessage());if (error instanceof WebClientResponseException) {WebClientResponseException webClientResponseException = (WebClientResponseException) error;int statusCode = webClientResponseException.getStatusCode().value();String statusText = webClientResponseException.getStatusText();System.err.println("Error status code: " + statusCode);System.err.println("Error status text: " + statusText);}
});
subscribe方法中的第二个lambda表达式检查错误是否是WebClientResponseException的实例,这是WebClient在服务器有错误响应时抛出的特定类型的异常。如果它是WebClientResponseException的实例,则代码将从异常中提取状态代码和状态文本,并将它们记录到日志中。
还可以根据发生的特定错误在此lambda表达式中添加其他错误处理逻辑。例如,你可以重试请求、回退到默认值或以特定方式记录错误。
(6) 处理成功响应和错误的完整代码:
responseMono.subscribe(
response -> {// handle the responseLOG.info("SUCCESS API Response {}", response);
},
error -> {// handle the errorLOG.error("An error occurred: {}", error.getMessage());LOG.error("error class: {}", error.getClass());// Errors / Exceptions from Serverif (error instanceof WebClientResponseException) {WebClientResponseException webClientResponseException =(WebClientResponseException) error;int statusCode = webClientResponseException.getStatusCode().value();String statusText = webClientResponseException.getStatusText();LOG.info("Error status code: {}", statusCode);LOG.info("Error status text: {}", statusText);if (statusCode >= 400 && statusCode < 500) {LOG.info("Error Response body {}", webClientResponseException.getResponseBodyAsString());}Throwable cause = webClientResponseException.getCause();LOG.error("webClientResponseException");if (null != cause) {LOG.info("Cause {}", cause.getClass());if (cause instanceof ReadTimeoutException) {LOG.error("ReadTimeout Exception");}if (cause instanceof TimeoutException) {LOG.error("Timeout Exception");}}}// Client errors i.e. Timeouts etc - if (error instanceof WebClientRequestException) {LOG.error("webClientRequestException");WebClientRequestException webClientRequestException =(WebClientRequestException) error;Throwable cause = webClientRequestException.getCause();if (null != cause) {LOG.info("Cause {}", cause.getClass());if (cause instanceof ReadTimeoutException) {LOG.error("ReadTimeout Exception");}if (cause instanceof ConnectTimeoutException) {LOG.error("Connect Timeout Exception");}}}
});
超时
我们可以在每个请求中设置超时,如下所示:
return webClient.method(this.httpMethod).uri(this.uri).headers(httpHeaders -> httpHeaders.addAll(additionalHeaders)).bodyValue(this.requestEntity).retrieve().bodyToMono(responseType).timeout(Duration.ofMillis(readTimeout)) // request timeout for this request.block();
但是,我们无法在每个请求中设置连接超时,这是WebClient 的属性,只能设置一次。如果需要,我们始终可以使用新的连接超时值创建一个新的 Web 客户端实例。
连接超时、读取超时和请求超时的区别如下:
结论
由于 RestTemplace 已弃用,开发人员应开始使用 WebClient 进行 REST 调用,非阻塞 I/O 调用肯定会提高应用程序性能。它不仅提供了许多其他令人兴奋的功能,例如改进的错误处理和对流的支持,而且如果需要,它还可以在阻塞模式下使用来模拟 RestTemplate 行为。
相关文章:

弃用 RestTemplate,来了解一下官方推荐的 WebClient !
在 Spring Framework 5.0 及更高版本中,RestTemplate 已被弃用,取而代之的是较新的 WebClient。这意味着虽然 RestTemplate 仍然可用,但鼓励 Spring 开发人员迁移到新项目的 WebClient。 WebClient 优于 RestTemplate 的原因有几个ÿ…...

西圣、倍思、品胜电容笔孰强孰弱?多维度对比测评三款平替电容笔
在近年来,平替电容笔以其亲民的价格和优质的性能变现,成为市场上备受追捧的选择。然而,某些品牌为了吸引消费者,降低价格的同时常采用劣质材料,结果握笔体验差,书写效果糟糕,甚至在长时间使用后…...

VS2022配置调试Qt源代码
需要保证源代码和项目使用的版本匹配,符号需要注意是64位还是32位,并且用msvc。 1. 设置源代码路径 2. 设置调试PDB路径 这里最好把4个地方都加进去,防止某些不常用PDB被漏掉。 D:\Qt\5.15.2\msvc2019_64\bin D:\Qt\5.15.2\msvc2019_64\lib…...
Python 的 Pygame 库来开发一个游戏
import pygame import random# 初始化 Pygame pygame.init()# 设置屏幕大小 screen_width 800 screen_height 600 screen pygame.display.set_mode((screen_width, screen_height))# 设置标题 pygame.display.set_caption("飞机大战")# 加载图片 player_img pyga…...

diffusion model 学习笔记
条件引导的 diffusion 对于无条件的DDPM 而言 p ( x t ∣ x 0 ) ∼ N ( α t ˉ x 0 , 1 − α t ˉ ⋅ I ) p(x_t | x_0) \sim \mathcal{N}( \sqrt{\bar{\alpha_t}} x_0, 1-\bar{\alpha_t} \cdot \mathrm{I} ) p(xt∣x0)∼N(αtˉ x0,1−αtˉ⋅I) 可以得到…...

【C++题解】1967. 输出字符的ascii码
欢迎关注本专栏《C从零基础到信奥赛入门级(CSP-J)》 问题:1967. 输出字符的ascii码 类型:字符串、字符型 题目描述: 从键盘读入一个字符,请计算并输出该字符的 ascii 码。 比如:字符 ‘A’ …...

Java入门9——类和对象+this+构造方法
终于终于,我们开启了Java的大门!类和对象才是Java梦开始的地方,让我们一起来学习吧! 一、类和对象 通常来讲,一个Java文件里面写一个类,那么类是什么呢? 类,字面意思,就…...

Django目录结构最佳实践
Django项目目录结构 项目目录结构配置文件引用修改创建自定义子应用方法修改自定义注册目录从apps目录开始 项目目录结构 └── backend # 后端项目目录(项目名称)├── __init__.py├── logs # 项目日志目录├── manage.py #…...
npm run dev 报错PS ‘vite‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
报错场景: 使用npm run dev命令启动项目,报错 PS D:\vuecode\ying-mei> npm run dev > ying-mei0.0.0 dev > vite vite 不是内部或外部命令,也不是可运行的程序 或批处理文件。 原因: 缺失依赖,因为新建的项…...

后端:Spring、Spring Boot-实例化Bean依赖注入(DI)
文章目录 1. 实例化Bean2. 使用FactoryBean3. 依赖注入(DI)3.1 AutoWired 属性注入(查找顺序:先类型,后名字)3.2 AutoWired 在构造函数&参数上的使用3.3 Inject和Resource 进行依赖注入3.4 Value 进行注入 1. 实例化Bean 默认使用无参构造函数&…...
C++ 数据结构 静态顺序表、动态顺序表。
静态顺序表(Static Array List)是一种线性数据结构,通常用数组实现。它具有固定的大小,并在编译时分配内存。以下是静态顺序表的一些基本概念和实现示例。 静态顺序表基本概念 固定大小:静态顺序表的大小在创建时定义…...

QML旋转选择器组件Tumbler
1. 介绍 Tumbler是一个用于创建旋转选择器的组件。它提供了一种直观的方式来让用户从一组选项中进行选择,类似于转盘式数字密码锁。网上找的类似网图如下: 在QML里,这种组件一共有两个版本,分别在QtQuick.Extras 1.4(旧)和QtQuic…...
在工作中常用到的 Linux 命令总结
引言 我之前找工作面试的时候。几乎每次面试几乎都会问到 Linux 常用命令,会问一些命令的应用场景。目的是考察我们是否在实际开发中经常用、用得熟练。今天我就来系统地总结一下开发过程中最常用的 Linux 命令,算是一个复习总结。 基本操作 文件管理…...

反射、枚举和lambda表达式
文章目录 一、反射1.1 什么是反射1.2 反射相关的类1.3 示例获取Class对象通过反射去创建对象调用带两个参数且私有的构造方法获取私有属性反射私有方法 二、枚举2.1 什么是枚举以及其优缺点2.2 如何使用枚举组织常量的基本方法枚举类的其他方法示例枚举能否通过反射拿到实例对象…...

Stable Diffusion Web UI 1.9.4常用插件扩展-WD14-tagger
Stable Diffusion Web UI 1.9.4 运行在 WSL 中的 Docker 容器中 tagger 插件的作用是,上传一张图片,反推这张图片可能的提示词。 使用场景就是,想要得到类似的图片内容时使用。 WD14-tagger 安装 Stable Diffusion WebUI WD14-tagger GitH…...
java 第19天 Lambda、jdk8.0新方法、Optional
一.Lambda表达式 前提是:参数是函数式接口才可以书写Lambda表达式 函数式接口条件: 1.接口 2.只有一个抽象方法 lambda表达式又称为匿名函数,允许匿名函数以参数的形式传入方法,简化代码 lambda表达式分为两部分()->{} …...

江协科技STM32学习- P31 I2C通信协议
🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝…...

6、liunx目录的功能
一、目录简介 Linux主要树结构目录包括:/、/root、/home、/usr、/bin、/tmp、/sbin、/proc、/boot 【Linux系统中常见目录功能如下】 / 根目录;/bin 存放必要的命令;(普通用户) /boot 存放内核以及启动所需的文件&…...
挑战Java面试题复习第5天,无人扶我青云志
挑战第 5 天 java反射List Set Map 区别Object 常用方法 java反射 定义: 运行时动态获取类信息和调用方法的特性。 应用场景: JDBC数据库连接。框架如Hibernate和Struts等。 实现方式: 通过四种方法获取Class对象: Class.fo…...

大规模语言模型:从理论到实践(1)
1、绪论 大规模语言模型(Large Language Models,LLM)是由包含数百亿以上参数的深度神经网络构建的语言模型,采用自监督学习方法通过大量无标注文本进行训练。自2018年以来,多个公司和研究机构相继发布了多种模型&#…...
多标签多分类 用什么函数激活
在多标签多分类任务中,激活函数的选择需要根据任务特性和输出层的设计来决定。以下是常见的激活函数及其适用场景: 一、多标签分类任务的特点 每个样本可以属于多个类别(标签之间非互斥,例如一篇文章可能同时属于 “科技” 和 “…...

iview组件库:当后台返回到的数据与使用官网组件指定的字段不匹配时,进行修改某个属性名再将response数据渲染到页面上的处理
1、需求导入 当存在前端需要的数据的字段渲染到表格或者是一些公共的表格组件展示数据时的某个字段名与后台返回的字段不一致时,那么需要前端进行稍加处理,而不能直接this.list res.data;这样数据是渲染不出来的。 2、后台返回的数据类型 Datalist(pn) …...

开源大模型网关:One API实现主流AI模型API的统一管理与分发
以下是对One API的简单介绍: One API是一个使用go语言开发的大语言模型 API 管理与分发系统支持Docker一键快速部署,且资源占用小,高性能开箱支持多平台大模型快速接入,包括OpenAI、Gemini、xAI、Grop、Anthropic Claude、Ollama…...
算法训练第十天
232. 用栈实现队列 代码: class MyQueue(object):def __init__(self):self.arr1 []self.arr2 []def push(self, x):""":type x: int:rtype: None"""self.arr1.append(x)def pop(self):""":rtype: int""…...

嵌入式SDK技术EasyRTC音视频实时通话助力即时通信社交/教育等多场景创新应用
一、引言 在数字化时代,即时通信已成为人们生活和工作中不可或缺的部分。音视频功能作为即时通信的核心,能实现更加直观、高效的信息传递。EasyRTC作为一款强大的实时通信框架,具备诸多优势,为即时通信的音视频应用提供了优质解…...
powershell 安装 .netframework3.5
在 PowerShell 中安装 .NET Framework 3.5 可以通过几种不同的方法实现,取决于你的操作系统版本。以下是几种常见的方法: 方法1:使用 DISM 命令 对于 Windows 10 和 Windows 8.1,你可以使用 DISM(Deployment Image Se…...

DAY43 复习日
浙大疏锦行-CSDN博客 kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:把项目拆分成多个文件 src/config.py: 用于存放项目配置,例如文件路径、学习率、批次大小等。 # src/config.py# Paths DATA_DIR "data…...
在网页加载时自动运行js的方法(2025最新)
在网页加载时自动运行JavaScript方法有多种实现方式,以下是常见的几种方法: 1. 使用 DOMContentLoaded 事件 当初始HTML文档完全加载和解析后触发(无需等待图片等资源加载): document.addEventListener(DOMC…...
第1章:Neo4j简介与图数据库基础
1.1 图数据库概述 在当今数据爆炸的时代,数据不仅仅是以量取胜,更重要的是数据之间的关联关系。传统的关系型数据库在处理高度关联数据时往往力不从心,而图数据库则应运而生,成为处理复杂关联数据的理想选择。 传统关系型数据库…...
数学建模期末速成 聚类分析与判别分析
聚类分析是在不知道有多少类别的前提下,建立某种规则对样本或变量进行分类。判别分析是已知类别,在已知训练样本的前提下,利用训练样本得到判别函数,然后对未知类别的测试样本判别其类别。 聚类分析 根据样本自身的属性…...