spring boot 3 restclient 完整教程
1. restclient 简介与环境准备
1.1 restclient 简介
restclient 是 spring framework 6 引入的新的 http 客户端。
作为 resttemplate 的现代替代方案,提供了更简洁的 api、更好的响应式支持和函数式编程风格。
在 spring boot 3 中,restclient 成为了推荐的 http 客户端选择。
相比 resttemplate,restclient 具有以下优势:
- 流畅的 api 设计,支持链式调用
- 更好的类型安全和错误处理
- 内置对 json 序列化/反序列化的支持
- 支持拦截器和请求/响应处理
- 与 spring 生态系统无缝集成
1.2 环境准备
1.2.1 开发环境
- jdk: 25
- maven: 3.9.11
- spring boot: 3.5.7
1.2.2 创建项目与依赖配置
pom.xml
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation="http://maven.apache.org/pom/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<!-- 父项目依赖 -->
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>3.5.7</version>
<relativepath/> <!-- lookup parent from repository -->
</parent>
<groupid>com.lihaozhe</groupid>
<artifactid>restclient-tutorial</artifactid>
<version>0.0.1</version>
<name>restclient-tutorial</name>
<description>spring boot 3 restclient tutorial</description>
<!-- 属性配置 -->
<properties>
<java.version>25</java.version>
</properties>
<!-- 依赖配置 -->
<dependencies>
<!-- spring boot web 依赖,包含 restclient -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<!-- json 处理 -->
<dependency>
<groupid>com.fasterxml.jackson.core</groupid>
<artifactid>jackson-core</artifactid>
</dependency>
<dependency>
<groupid>com.fasterxml.jackson.core</groupid>
<artifactid>jackson-annotations</artifactid>
</dependency>
<dependency>
<groupid>com.fasterxml.jackson.core</groupid>
<artifactid>jackson-databind</artifactid>
</dependency>
<dependency>
<groupid>com.fasterxml.jackson.datatype</groupid>
<artifactid>jackson-datatype-jsr310</artifactid>
</dependency>
<!-- 读取配置文件 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-configuration-processor</artifactid>
<optional>true</optional>
</dependency>
<!-- lombok 简化代码 -->
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<configuration>
<annotationprocessorpaths>
<path>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-configuration-processor</artifactid>
</path>
<path>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
</path>
</annotationprocessorpaths>
</configuration>
</plugin>
<plugin>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-maven-plugin</artifactid>
<configuration>
<excludes>
<exclude>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>1.2.3 restclient 配置
创建一个配置类,用于配置 restclient 实例:
restclientconfig.java
package com.lihaozhe.restclienttutorial.config;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.http.client.jdkclienthttprequestfactory;
import org.springframework.web.client.restclient;
@configuration
public class restclientconfig {
// 配置默认的 restclient 实例
@bean
public restclient restclient() {
// 创建 restclient 构建器
return restclient.builder()
// 设置默认基础 url
.baseurl("https://jsonplaceholder.typicode.com")
// 设置请求工厂,这里使用 jdk 自带的 httpclient
.requestfactory(new jdkclienthttprequestfactory())
// 构建 restclient 实例
.build();
}
}1.2.4 启动类
restclienttutorialapplication.java
package com.lihaozhe.restclienttutorial;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@springbootapplication
public class restclienttutorialapplication {
public static void main(string[] args) {
springapplication.run(restclienttutorialapplication.class, args);
}
}2. restclient 基础使用
2.1 数据模型定义
首先定义一个示例数据模型,用于后续的 api 调用:
user.java
package com.lihaozhe.restclienttutorial.model;
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;
// 使用 lombok 注解简化代码
@data
@noargsconstructor
@allargsconstructor
public class user {
private long id;
private string name;
private string username;
private string email;
private address address;
private string phone;
private string website;
private company company;
}
@data
@noargsconstructor
@allargsconstructor
class address {
private string street;
private string suite;
private string city;
private string zipcode;
private geo geo;
}
@data
@noargsconstructor
@allargsconstructor
class geo {
private string lat;
private string lng;
}
@data
@noargsconstructor
@allargsconstructor
class company {
private string name;
private string catchphrase;
private string bs;
}post.java
package com.lihaozhe.restclienttutorial.model;
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;
@data
@noargsconstructor
@allargsconstructor
public class post {
private long id;
private long userid;
private string title;
private string body;
}2.2 基本 http 方法使用
创建一个服务类,演示 restclient 的基本用法:
apiservice.java
package com.lihaozhe.restclienttutorial.service;
import com.lihaozhe.restclienttutorial.model.post;
import com.lihaozhe.restclienttutorial.model.user;
import lombok.requiredargsconstructor;
import org.springframework.http.httpstatus;
import org.springframework.stereotype.service;
import org.springframework.web.client.restclient;
import java.util.list;
@service
// 构造函数注入所需依赖
@requiredargsconstructor
public class apiservice {
// 注入 restclient 实例
private final restclient restclient;
/**
* 使用 get 方法获取单个用户
*/
public user getuserbyid(long id) {
// 发送 get 请求并返回 user 对象
return restclient.get()
// 指定请求路径
.uri("/users/{id}", id)
// 发送请求并将响应体转换为 user 类型
.retrieve()
// 处理 http 状态码 404 的情况
.onstatus(httpstatus.not_found, (request, response) -> {
throw new runtimeexception("user not found with id: " + id);
})
// 将响应体转换为 user 对象
.body(user.class);
}
/**
* 使用 get 方法获取所有用户
*/
public list<user> getallusers() {
// 发送 get 请求并返回用户列表
return restclient.get()
.uri("/users")
.retrieve()
// 由于返回的是数组,使用参数化类型
.body(user[].class);
}
/**
* 使用 post 方法创建新帖子
*/
public post createpost(post post) {
// 发送 post 请求创建新资源
return restclient.post()
.uri("/posts")
// 设置请求体
.body(post)
.retrieve()
// 处理 201 created 状态码
.onstatus(httpstatus.created, (request, response) -> {
system.out.println("post created successfully");
})
.body(post.class);
}
/**
* 使用 put 方法更新帖子
*/
public post updatepost(long id, post post) {
// 发送 put 请求更新资源
return restclient.put()
.uri("/posts/{id}", id)
.body(post)
.retrieve()
.body(post.class);
}
/**
* 使用 delete 方法删除帖子
*/
public void deletepost(long id) {
// 发送 delete 请求删除资源
restclient.delete()
.uri("/posts/{id}", id)
.retrieve()
// 检查响应状态码是否为 200 ok
.tobodilessentity();
}
}2.3 测试 restclient 基础功能
创建测试类验证上述功能:
apiservicetest.java
package com.lihaozhe.restclienttutorial.service;
import com.lihaozhe.restclienttutorial.model.post;
import com.lihaozhe.restclienttutorial.model.user;
import org.junit.jupiter.api.test;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.boot.test.context.springboottest;
import java.util.list;
import static org.junit.jupiter.api.assertions.*;
@springboottest
class apiservicetest {
@autowired
private apiservice apiservice;
@test
void shouldgetuserbyid() {
// 测试获取单个用户
user user = apiservice.getuserbyid(1l);
assertnotnull(user);
assertequals(1l, user.getid());
assertequals("leanne graham", user.getname());
}
@test
void shouldgetallusers() {
// 测试获取所有用户
list<user> users = apiservice.getallusers();
assertnotnull(users);
assertfalse(users.isempty());
asserttrue(users.size() > 0);
}
@test
void shouldcreatepost() {
// 测试创建帖子
post post = new post(null, 1l, "test title", "test body");
post createdpost = apiservice.createpost(post);
assertnotnull(createdpost);
assertnotnull(createdpost.getid());
assertequals(post.gettitle(), createdpost.gettitle());
assertequals(post.getbody(), createdpost.getbody());
}
@test
void shouldupdatepost() {
// 测试更新帖子
long postid = 1l;
post post = new post(postid, 1l, "updated title", "updated body");
post updatedpost = apiservice.updatepost(postid, post);
assertnotnull(updatedpost);
assertequals(postid, updatedpost.getid());
assertequals(post.gettitle(), updatedpost.gettitle());
}
@test
void shoulddeletepost() {
// 测试删除帖子
assertdoesnotthrow(() -> apiservice.deletepost(1l));
}
}3. restclient 高级特性
3.1 请求参数与 headers 设置
apiservice.java (扩展)
/**
* 演示请求参数和 headers 设置
*/
public list<post> getpostsbyuserid(long userid) {
// 设置请求参数和 headers
return restclient.get()
// 使用 uri 方法设置查询参数
.uri(uribuilder -> uribuilder
.path("/posts")
.queryparam("userid", userid)
.build())
// 设置请求头
.header("accept", "application/json")
.header("authorization", "bearer token123")
.retrieve()
.body(post[].class);
}3.2 拦截器使用
创建一个自定义拦截器,用于日志记录:
logginginterceptor.java
package com.lihaozhe.restclienttutorial.interceptor;
import org.springframework.http.httprequest;
import org.springframework.http.client.clienthttprequestexecution;
import org.springframework.http.client.clienthttprequestinterceptor;
import org.springframework.http.client.clienthttpresponse;
import org.springframework.util.streamutils;
import java.io.ioexception;
import java.nio.charset.standardcharsets;
public class logginginterceptor implements clienthttprequestinterceptor {
@override
public clienthttpresponse intercept(
httprequest request,
byte[] body,
clienthttprequestexecution execution
) throws ioexception {
// 记录请求信息
logrequest(request, body);
// 执行请求
clienthttpresponse response = execution.execute(request, body);
// 记录响应信息
logresponse(response);
return response;
}
private void logrequest(httprequest request, byte[] body) {
system.out.println("=== request ===");
system.out.println("method: " + request.getmethod());
system.out.println("uri: " + request.geturi());
system.out.println("headers: " + request.getheaders());
system.out.println("body: " + new string(body, standardcharsets.utf_8));
system.out.println("===============");
}
private void logresponse(clienthttpresponse response) throws ioexception {
system.out.println("=== response ===");
system.out.println("status code: " + response.getstatuscode());
system.out.println("headers: " + response.getheaders());
system.out.println("body: " + streamutils.copytostring(response.getbody(), standardcharsets.utf_8));
system.out.println("===============");
}
}更新 restclient 配置,添加拦截器:
restclientconfig.java (扩展)
// 配置带有拦截器的 restclient
@bean
public restclient restclientwithinterceptor() {
return restclient.builder()
.baseurl("https://jsonplaceholder.typicode.com")
.requestfactory(new jdkclienthttprequestfactory())
// 添加自定义拦截器
.interceptors(new logginginterceptor())
.build();
}
3.3 错误处理
apiservice.java (扩展)
/**
* 演示高级错误处理
*/
public user getuserwitherrorhandling(long id) {
try {
return restclient.get()
.uri("/users/{id}", id)
.retrieve()
// 处理 4xx 客户端错误
.onstatus(httpstatus::is4xxclienterror, (request, response) -> {
string errorbody = streamutils.copytostring(response.getbody(), standardcharsets.utf_8);
throw new runtimeexception("client error: " + response.getstatuscode() + ", body: " + errorbody);
})
// 处理 5xx 服务器错误
.onstatus(httpstatus::is5xxservererror, (request, response) -> {
throw new runtimeexception("server error: " + response.getstatuscode());
})
.body(user.class);
} catch (restclientexception e) {
// 捕获并处理 restclient 异常
system.err.println("error fetching user: " + e.getmessage());
// 可以根据需要返回默认值或重新抛出
return new user();
}
}3.4 超时设置
更新 restclient 配置,添加超时设置:
restclientconfig.java (扩展)
// 配置带有超时设置的 restclient
@bean
public restclient restclientwithtimeout() {
// 创建 http 客户端工厂并设置超时
jdkclienthttprequestfactory requestfactory = new jdkclienthttprequestfactory();
// 设置连接超时
requestfactory.setconnecttimeout(duration.ofseconds(5));
// 设置读取超时
requestfactory.setreadtimeout(duration.ofseconds(10));
return restclient.builder()
.baseurl("https://jsonplaceholder.typicode.com")
.requestfactory(requestfactory)
.build();
}4. 实战案例:restful api 客户端实现
4.1 案例说明
我们将实现一个完整的 restful api 客户端,用于管理"任务"资源,包括以下功能:
- 获取所有任务
- 获取单个任务
- 创建任务
- 更新任务
- 删除任务
- 根据状态筛选任务
4.2 数据模型
task.java
package com.lihaozhe.restclienttutorial.model;
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;
import java.time.localdatetime;
@data
@noargsconstructor
@allargsconstructor
public class task {
private long id;
private string title;
private string description;
private boolean completed;
private localdatetime createdat;
private localdatetime updatedat;
}4.3 服务接口
taskservice.java
package com.lihaozhe.restclienttutorial.service;
import com.lihaozhe.restclienttutorial.model.task;
import java.util.list;
public interface taskservice {
list<task> getalltasks();
task gettaskbyid(long id);
task createtask(task task);
task updatetask(long id, task task);
void deletetask(long id);
list<task> gettasksbystatus(boolean completed);
}4.4 服务实现
taskserviceimpl.java
package com.lihaozhe.restclienttutorial.service;
import com.lihaozhe.restclienttutorial.model.task;
import lombok.requiredargsconstructor;
import org.springframework.http.httpstatus;
import org.springframework.stereotype.service;
import org.springframework.web.client.restclient;
import java.util.list;
@service
@requiredargsconstructor
public class taskserviceimpl implements taskservice {
private final restclient restclient;
// 任务 api 的基础路径
private static final string tasks_base_url = "/tasks";
@override
public list<task> getalltasks() {
return restclient.get()
.uri(tasks_base_url)
.retrieve()
.body(task[].class);
}
@override
public task gettaskbyid(long id) {
return restclient.get()
.uri(tasks_base_url + "/{id}", id)
.retrieve()
.onstatus(httpstatus.not_found, (request, response) -> {
throw new runtimeexception("task not found with id: " + id);
})
.body(task.class);
}
@override
public task createtask(task task) {
// 设置创建时间
task.setcreatedat(localdatetime.now());
task.setupdatedat(localdatetime.now());
return restclient.post()
.uri(tasks_base_url)
.body(task)
.retrieve()
.onstatus(httpstatus.created, (request, response) -> {
system.out.println("task created successfully");
})
.body(task.class);
}
@override
public task updatetask(long id, task task) {
// 设置更新时间
task.setupdatedat(localdatetime.now());
return restclient.put()
.uri(tasks_base_url + "/{id}", id)
.body(task)
.retrieve()
.body(task.class);
}
@override
public void deletetask(long id) {
restclient.delete()
.uri(tasks_base_url + "/{id}", id)
.retrieve()
.tobodilessentity();
}
@override
public list<task> gettasksbystatus(boolean completed) {
return restclient.get()
.uri(uribuilder -> uribuilder
.path(tasks_base_url)
.queryparam("completed", completed)
.build())
.retrieve()
.body(task[].class);
}
}4.5 控制器层
taskcontroller.java
package com.lihaozhe.restclienttutorial.controller;
import com.lihaozhe.restclienttutorial.model.task;
import com.lihaozhe.restclienttutorial.service.taskservice;
import lombok.requiredargsconstructor;
import org.springframework.http.httpstatus;
import org.springframework.http.responseentity;
import org.springframework.web.bind.annotation.*;
import java.util.list;
@restcontroller
@requestmapping("/api/tasks")
@requiredargsconstructor
public class taskcontroller {
private final taskservice taskservice;
@getmapping
public list<task> getalltasks() {
return taskservice.getalltasks();
}
@getmapping("/{id}")
public responseentity<task> gettaskbyid(@pathvariable long id) {
try {
task task = taskservice.gettaskbyid(id);
return responseentity.ok(task);
} catch (runtimeexception e) {
return responseentity.notfound().build();
}
}
@postmapping
public responseentity<task> createtask(@requestbody task task) {
task createdtask = taskservice.createtask(task);
return new responseentity<>(createdtask, httpstatus.created);
}
@putmapping("/{id}")
public responseentity<task> updatetask(
@pathvariable long id,
@requestbody task task
) {
task updatedtask = taskservice.updatetask(id, task);
return responseentity.ok(updatedtask);
}
@deletemapping("/{id}")
public responseentity<void> deletetask(@pathvariable long id) {
taskservice.deletetask(id);
return responseentity.nocontent().build();
}
@getmapping("/filter")
public list<task> gettasksbystatus(@requestparam boolean completed) {
return taskservice.gettasksbystatus(completed);
}
}4.6 测试用例
taskserviceimpltest.java
package com.lihaozhe.restclienttutorial.service;
import com.lihaozhe.restclienttutorial.model.task;
import org.junit.jupiter.api.test;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.boot.test.context.springboottest;
import org.springframework.web.client.restclient;
import java.time.localdatetime;
import java.util.list;
import static org.junit.jupiter.api.assertions.*;
@springboottest
class taskserviceimpltest {
@autowired
private taskservice taskservice;
// 注入带有拦截器的 restclient 用于测试
@autowired
@qualifier("restclientwithinterceptor")
private restclient restclient;
@test
void shouldperformcrudoperations() {
// 创建任务
task task = new task(
null,
"test task",
"test description",
false,
null,
null
);
task createdtask = taskservice.createtask(task);
assertnotnull(createdtask);
assertnotnull(createdtask.getid());
assertequals(task.gettitle(), createdtask.gettitle());
assertnotnull(createdtask.getcreatedat());
// 获取任务
long taskid = createdtask.getid();
task fetchedtask = taskservice.gettaskbyid(taskid);
assertnotnull(fetchedtask);
assertequals(taskid, fetchedtask.getid());
// 更新任务
fetchedtask.setcompleted(true);
fetchedtask.settitle("updated task title");
task updatedtask = taskservice.updatetask(taskid, fetchedtask);
assertnotnull(updatedtask);
asserttrue(updatedtask.iscompleted());
assertequals("updated task title", updatedtask.gettitle());
asserttrue(updatedtask.getupdatedat().isafter(createdtask.getcreatedat()));
// 按状态筛选任务
list<task> completedtasks = taskservice.gettasksbystatus(true);
assertfalse(completedtasks.isempty());
// 删除任务
assertdoesnotthrow(() -> taskservice.deletetask(taskid));
// 验证任务已删除
assertthrows(runtimeexception.class, () -> taskservice.gettaskbyid(taskid));
}
}5. 最佳实践与性能优化
5.1 最佳实践
- 单一职责原则:每个 restclient 实例专注于特定的 api 或服务
- 异常处理:始终处理可能的异常,提供有意义的错误信息
- 连接池管理:合理配置连接池大小和超时时间
- 避免重复创建:restclient 实例应该是单例的,避免频繁创建和销毁
- 日志记录:使用拦截器记录关键请求和响应信息,便于调试
- 配置外部化:将基础 url、超时时间等配置放在配置文件中
- 使用 builders:利用 restclient 的构建器模式创建客户端实例
5.2 性能优化
- 连接池配置:
@bean
public restclient restclientwithpool() {
// 配置 http 客户端连接池
httpclient httpclient = httpclient.newbuilder()
.connecttimeout(duration.ofseconds(5))
.connectionpool(new connectionpool(10, 30, timeunit.seconds))
.build();
jdkclienthttprequestfactory requestfactory = new jdkclienthttprequestfactory(httpclient);
return restclient.builder()
.baseurl("https://api.lihaozhe.com")
.requestfactory(requestfactory)
.build();
}- 响应压缩:启用请求和响应压缩
@bean
public restclient restclientwithcompression() {
httpclient httpclient = httpclient.newbuilder()
.connecttimeout(duration.ofseconds(5))
.build();
jdkclienthttprequestfactory requestfactory = new jdkclienthttprequestfactory(httpclient);
return restclient.builder()
.baseurl("https://api.lihaozhe.com")
.requestfactory(requestfactory)
.defaultheader("accept-encoding", "gzip, deflate")
.build();
}- 异步请求:对于非阻塞场景,使用异步请求
@bean
public asyncrestclient asyncrestclient() {
return asyncrestclient.builder()
.baseurl("https://api.lihaozhe.com")
.build();
}
// 使用示例
public completablefuture<user> getuserasync(long id) {
return asyncrestclient.get()
.uri("/users/{id}", id)
.retrieve()
.bodytomono(user.class)
.tofuture();
}- 缓存策略:对于频繁访问且不常变化的资源,实现缓存机制
private final cache<string, user> usercache = cachebuilder.newbuilder()
.expireafterwrite(10, timeunit.minutes)
.maximumsize(100)
.build();
public user getcacheduser(long id) {
try {
return usercache.get(id.tostring(), () -> {
// 缓存未命中时,从 api 获取
return restclient.get()
.uri("/users/{id}", id)
.retrieve()
.body(user.class);
});
} catch (executionexception e) {
throw new runtimeexception("error fetching user", e.getcause());
}
}5.3 配置外部化
application.properties
# api 基础 url api.base-url=https://jsonplaceholder.typicode.com # 连接超时(毫秒) api.connection-timeout=5000 # 读取超时(毫秒) api.read-timeout=10000 # 连接池大小 api.connection-pool-size=10
配置类
@configuration
@configurationproperties(prefix = "api")
@data
public class apiproperties {
private string baseurl;
private int connectiontimeout;
private int readtimeout;
private int connectionpoolsize;
}
@configuration
public class configuredrestclientconfig {
@bean
public restclient configuredrestclient(apiproperties apiproperties) {
httpclient httpclient = httpclient.newbuilder()
.connecttimeout(duration.ofmillis(apiproperties.getconnectiontimeout()))
.connectionpool(new connectionpool(
apiproperties.getconnectionpoolsize(),
30,
timeunit.seconds
))
.build();
jdkclienthttprequestfactory requestfactory = new jdkclienthttprequestfactory(httpclient);
requestfactory.setreadtimeout(duration.ofmillis(apiproperties.getreadtimeout()));
return restclient.builder()
.baseurl(apiproperties.getbaseurl())
.requestfactory(requestfactory)
.interceptors(new logginginterceptor())
.build();
}
}总结
本教程详细介绍了 spring boot 3 中 restclient 的使用方法,从基础到高级,涵盖了各种常见场景和最佳实践。restclient 作为 resttemplate 的现代替代方案,提供了更简洁、更灵活的 api,是开发 restful 客户端的理想选择。
通过本教程,你应该能够:
- 理解 restclient 的核心概念和优势
- 配置和使用 restclient 发送各种 http 请求
- 处理请求参数、headers 和响应数据
- 使用拦截器、错误处理等高级特性
- 实现完整的 restful api 客户端
- 应用最佳实践和性能优化技巧
restclient 结合了 spring 的强大功能和现代 java 的特性,为开发者提供了高效、可靠的 http 客户端解决方案。在实际项目中,应根据具体需求选择合适的配置和功能,以获得最佳的性能和可维护性。
到此这篇关于spring boot 3 restclient使用实战案例的文章就介绍到这了,更多相关springboot restclient使用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论