当前位置: 首页 > news >正文

弃用 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&param2=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 及更高版本中&#xff0c;RestTemplate 已被弃用&#xff0c;取而代之的是较新的 WebClient。这意味着虽然 RestTemplate 仍然可用&#xff0c;但鼓励 Spring 开发人员迁移到新项目的 WebClient。 WebClient 优于 RestTemplate 的原因有几个&#xff…...

西圣、倍思、品胜电容笔孰强孰弱?多维度对比测评三款平替电容笔

在近年来&#xff0c;平替电容笔以其亲民的价格和优质的性能变现&#xff0c;成为市场上备受追捧的选择。然而&#xff0c;某些品牌为了吸引消费者&#xff0c;降低价格的同时常采用劣质材料&#xff0c;结果握笔体验差&#xff0c;书写效果糟糕&#xff0c;甚至在长时间使用后…...

VS2022配置调试Qt源代码

需要保证源代码和项目使用的版本匹配&#xff0c;符号需要注意是64位还是32位&#xff0c;并且用msvc。 1. 设置源代码路径 2. 设置调试PDB路径 这里最好把4个地方都加进去&#xff0c;防止某些不常用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从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1967. 输出字符的ascii码 类型&#xff1a;字符串、字符型 题目描述&#xff1a; 从键盘读入一个字符&#xff0c;请计算并输出该字符的 ascii 码。 比如&#xff1a;字符 ‘A’ …...

Java入门9——类和对象+this+构造方法

终于终于&#xff0c;我们开启了Java的大门&#xff01;类和对象才是Java梦开始的地方&#xff0c;让我们一起来学习吧&#xff01; 一、类和对象 通常来讲&#xff0c;一个Java文件里面写一个类&#xff0c;那么类是什么呢&#xff1f; 类&#xff0c;字面意思&#xff0c;就…...

Django目录结构最佳实践

Django项目目录结构 项目目录结构配置文件引用修改创建自定义子应用方法修改自定义注册目录从apps目录开始 项目目录结构 └── backend # 后端项目目录&#xff08;项目名称&#xff09;├── __init__.py├── logs # 项目日志目录├── manage.py #…...

npm run dev 报错PS ‘vite‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

报错场景&#xff1a; 使用npm run dev命令启动项目&#xff0c;报错 PS D:\vuecode\ying-mei> npm run dev > ying-mei0.0.0 dev > vite vite 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 原因&#xff1a; 缺失依赖&#xff0c;因为新建的项…...

后端:Spring、Spring Boot-实例化Bean依赖注入(DI)

文章目录 1. 实例化Bean2. 使用FactoryBean3. 依赖注入(DI)3.1 AutoWired 属性注入(查找顺序&#xff1a;先类型&#xff0c;后名字)3.2 AutoWired 在构造函数&参数上的使用3.3 Inject和Resource 进行依赖注入3.4 Value 进行注入 1. 实例化Bean 默认使用无参构造函数&…...

C++ 数据结构 静态顺序表、动态顺序表。

静态顺序表&#xff08;Static Array List&#xff09;是一种线性数据结构&#xff0c;通常用数组实现。它具有固定的大小&#xff0c;并在编译时分配内存。以下是静态顺序表的一些基本概念和实现示例。 静态顺序表基本概念 固定大小&#xff1a;静态顺序表的大小在创建时定义…...

QML旋转选择器组件Tumbler

1. 介绍 Tumbler是一个用于创建旋转选择器的组件。它提供了一种直观的方式来让用户从一组选项中进行选择&#xff0c;类似于转盘式数字密码锁。网上找的类似网图如下&#xff1a; 在QML里&#xff0c;这种组件一共有两个版本&#xff0c;分别在QtQuick.Extras 1.4(旧)和QtQuic…...

在工作中常用到的 Linux 命令总结

引言 我之前找工作面试的时候。几乎每次面试几乎都会问到 Linux 常用命令&#xff0c;会问一些命令的应用场景。目的是考察我们是否在实际开发中经常用、用得熟练。今天我就来系统地总结一下开发过程中最常用的 Linux 命令&#xff0c;算是一个复习总结。 基本操作 文件管理…...

反射、枚举和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 插件的作用是&#xff0c;上传一张图片&#xff0c;反推这张图片可能的提示词。 使用场景就是&#xff0c;想要得到类似的图片内容时使用。 WD14-tagger 安装 Stable Diffusion WebUI WD14-tagger GitH…...

java 第19天 Lambda、jdk8.0新方法、Optional

一.Lambda表达式 前提是&#xff1a;参数是函数式接口才可以书写Lambda表达式 函数式接口条件&#xff1a; 1.接口 2.只有一个抽象方法 lambda表达式又称为匿名函数&#xff0c;允许匿名函数以参数的形式传入方法&#xff0c;简化代码 lambda表达式分为两部分()->{} …...

江协科技STM32学习- P31 I2C通信协议

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…...

6、liunx目录的功能

一、目录简介 Linux主要树结构目录包括&#xff1a;/、/root、/home、/usr、/bin、/tmp、/sbin、/proc、/boot 【Linux系统中常见目录功能如下】 / 根目录&#xff1b;/bin 存放必要的命令&#xff1b;&#xff08;普通用户&#xff09; /boot 存放内核以及启动所需的文件&…...

挑战Java面试题复习第5天,无人扶我青云志

挑战第 5 天 java反射List Set Map 区别Object 常用方法 java反射 定义&#xff1a; 运行时动态获取类信息和调用方法的特性。 应用场景&#xff1a; JDBC数据库连接。框架如Hibernate和Struts等。 实现方式&#xff1a; 通过四种方法获取Class对象&#xff1a; Class.fo…...

大规模语言模型:从理论到实践(1)

1、绪论 大规模语言模型&#xff08;Large Language Models&#xff0c;LLM&#xff09;是由包含数百亿以上参数的深度神经网络构建的语言模型&#xff0c;采用自监督学习方法通过大量无标注文本进行训练。自2018年以来&#xff0c;多个公司和研究机构相继发布了多种模型&#…...

告别杀后台!深度评测Ba-KeepAlive-U:这款UniAppX安卓保活插件到底有多强?(附多机型测试结果)

Ba-KeepAlive-U技术解析&#xff1a;如何为UniAppX应用实现跨机型保活方案 在移动应用开发领域&#xff0c;后台进程存活率一直是困扰开发者的技术难题。尤其对于需要持续运行定位、即时通讯或数据同步功能的应用&#xff0c;系统资源管理策略导致的"杀后台"现象直接…...

GD32F407定时器实战:1ms中断精准控制LED闪烁(附源码与调试技巧)

GD32F407定时器实战&#xff1a;1ms中断精准控制LED闪烁&#xff08;附源码与调试技巧&#xff09; 1. 嵌入式定时器的核心价值与应用场景 在嵌入式系统开发中&#xff0c;定时器如同系统的心跳&#xff0c;为各类周期性任务提供精准的时间基准。以智能家居中的温控系统为例&…...

如何通过自动化硬件适配技术突破Hackintosh配置瓶颈:OpCore Simplify技术深度解析

如何通过自动化硬件适配技术突破Hackintosh配置瓶颈&#xff1a;OpCore Simplify技术深度解析 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在开源系…...

【AI+实战】零基础部署私人ChatGPT网站:从NextChat到功能定制

1. 为什么你需要一个私人ChatGPT网站&#xff1f; 最近两年AI对话机器人的火爆程度&#xff0c;相信大家都有目共睹。但你是否遇到过这些问题&#xff1a;公共平台经常排队、担心隐私泄露、或者想要定制专属功能&#xff1f;这就是为什么越来越多的个人和小团队开始搭建自己的C…...

GLM-4.1V-9B-Base效果展示:艺术画作风格+主题+文化元素三重解析

GLM-4.1V-9B-Base效果展示&#xff1a;艺术画作风格主题文化元素三重解析 1. 视觉理解新标杆&#xff1a;GLM-4.1V-9B-Base简介 GLM-4.1V-9B-Base是智谱开源的一款视觉多模态理解模型&#xff0c;专为图像内容识别、场景描述和目标问答任务而设计。不同于普通的图像识别工具&…...

GLM-4-9B-Chat-1M惊艳效果:碳中和白皮书(120页)中的技术路径拆解、时间节点校验与政策匹配度评分

GLM-4-9B-Chat-1M惊艳效果&#xff1a;碳中和白皮书&#xff08;120页&#xff09;中的技术路径拆解、时间节点校验与政策匹配度评分 1. 项目背景与核心能力 今天要给大家展示一个让人眼前一亮的技术应用场景——用GLM-4-9B-Chat-1M这个本地部署的大模型&#xff0c;来深度分…...

Kettle转换里‘阻塞数据’控件为啥不灵?我用这个真实ETL案例给你讲透

Kettle转换中‘阻塞数据’控件的实战解析&#xff1a;从失效到精准控制 在ETL工具Kettle的实际应用中&#xff0c;数据流的精确控制往往是决定任务成败的关键。许多中高级用户在使用"阻塞数据直到步骤都完成"控件时&#xff0c;都曾遇到过看似配置正确却无法生效的困…...

Local AI MusicGen商业应用:电商视频智能配乐

Local AI MusicGen商业应用&#xff1a;电商视频智能配乐 你是不是也遇到过这样的烦恼&#xff1f;制作电商短视频时&#xff0c;翻遍了免费音乐库&#xff0c;要么版权有问题&#xff0c;要么风格不搭&#xff0c;要么就是千篇一律的背景音。自己配乐&#xff1f;没那个时间和…...

避坑指南:微信支付V3 SDK自动更新证书失败的5种常见原因及修复方法

微信支付V3证书自动更新失败排查手册&#xff1a;从原理到实战修复 微信支付的V3版本SDK以其自动证书更新机制著称&#xff0c;但不少开发者在集成过程中都遭遇过AutoUpdateCertificatesVerifier的失败问题。证书更新失败不仅会导致支付功能中断&#xff0c;还可能引发验签错误…...

Wan2.2-I2V-A14B部署教程:解决OOM/驱动报错/端口冲突三大常见问题

Wan2.2-I2V-A14B部署教程&#xff1a;解决OOM/驱动报错/端口冲突三大常见问题 1. 镜像概述与核心优势 Wan2.2-I2V-A14B是一款专为文生视频任务优化的私有部署镜像&#xff0c;特别针对RTX 4090D 24GB显存配置进行了深度优化。这个镜像最大的特点是解决了AI视频生成领域常见的…...