Spring Boot 3 RestClient使用實(shí)戰(zhàn)案例
Spring Boot 3 RestClient 完整教程
1. RestClient 簡介與環(huán)境準(zhǔn)備
1.1 RestClient 簡介
RestClient 是 Spring Framework 6 引入的新的 HTTP 客戶端。
作為 RestTemplate 的現(xiàn)代替代方案,提供了更簡潔的 API、更好的響應(yīng)式支持和函數(shù)式編程風(fēng)格。
在 Spring Boot 3 中,RestClient 成為了推薦的 HTTP 客戶端選擇。
相比 RestTemplate,RestClient 具有以下優(yōu)勢:
- 流暢的 API 設(shè)計(jì),支持鏈?zhǔn)秸{(diào)用
- 更好的類型安全和錯(cuò)誤處理
- 內(nèi)置對 JSON 序列化/反序列化的支持
- 支持?jǐn)r截器和請求/響應(yīng)處理
- 與 Spring 生態(tài)系統(tǒng)無縫集成
1.2 環(huán)境準(zhǔn)備
1.2.1 開發(fā)環(huán)境
- JDK: 25
- Maven: 3.9.11
- Spring Boot: 3.5.7
1.2.2 創(chuàng)建項(xiàng)目與依賴配置
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>
<!-- 父項(xiàng)目依賴 -->
<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 配置
創(chuàng)建一個(gè)配置類,用于配置 RestClient 實(shí)例:
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 {
// 配置默認(rèn)的 RestClient 實(shí)例
@Bean
public RestClient restClient() {
// 創(chuàng)建 RestClient 構(gòu)建器
return RestClient.builder()
// 設(shè)置默認(rèn)基礎(chǔ) URL
.baseUrl("https://jsonplaceholder.typicode.com")
// 設(shè)置請求工廠,這里使用 JDK 自帶的 HttpClient
.requestFactory(new JdkClientHttpRequestFactory())
// 構(gòu)建 RestClient 實(shí)例
.build();
}
}1.2.4 啟動(dòng)類
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 基礎(chǔ)使用
2.1 數(shù)據(jù)模型定義
首先定義一個(gè)示例數(shù)據(jù)模型,用于后續(xù)的 API 調(diào)用:
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 方法使用
創(chuàng)建一個(gè)服務(wù)類,演示 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
// 構(gòu)造函數(shù)注入所需依賴
@RequiredArgsConstructor
public class ApiService {
// 注入 RestClient 實(shí)例
private final RestClient restClient;
/**
* 使用 GET 方法獲取單個(gè)用戶
*/
public User getUserById(Long id) {
// 發(fā)送 GET 請求并返回 User 對象
return restClient.get()
// 指定請求路徑
.uri("/users/{id}", id)
// 發(fā)送請求并將響應(yīng)體轉(zhuǎn)換為 User 類型
.retrieve()
// 處理 HTTP 狀態(tài)碼 404 的情況
.onStatus(HttpStatus.NOT_FOUND, (request, response) -> {
throw new RuntimeException("User not found with id: " + id);
})
// 將響應(yīng)體轉(zhuǎn)換為 User 對象
.body(User.class);
}
/**
* 使用 GET 方法獲取所有用戶
*/
public List<User> getAllUsers() {
// 發(fā)送 GET 請求并返回用戶列表
return restClient.get()
.uri("/users")
.retrieve()
// 由于返回的是數(shù)組,使用參數(shù)化類型
.body(User[].class);
}
/**
* 使用 POST 方法創(chuàng)建新帖子
*/
public Post createPost(Post post) {
// 發(fā)送 POST 請求創(chuàng)建新資源
return restClient.post()
.uri("/posts")
// 設(shè)置請求體
.body(post)
.retrieve()
// 處理 201 Created 狀態(tài)碼
.onStatus(HttpStatus.CREATED, (request, response) -> {
System.out.println("Post created successfully");
})
.body(Post.class);
}
/**
* 使用 PUT 方法更新帖子
*/
public Post updatePost(Long id, Post post) {
// 發(fā)送 PUT 請求更新資源
return restClient.put()
.uri("/posts/{id}", id)
.body(post)
.retrieve()
.body(Post.class);
}
/**
* 使用 DELETE 方法刪除帖子
*/
public void deletePost(Long id) {
// 發(fā)送 DELETE 請求刪除資源
restClient.delete()
.uri("/posts/{id}", id)
.retrieve()
// 檢查響應(yīng)狀態(tài)碼是否為 200 OK
.toBodilessEntity();
}
}2.3 測試 RestClient 基礎(chǔ)功能
創(chuàng)建測試類驗(yàn)證上述功能:
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() {
// 測試獲取單個(gè)用戶
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() {
// 測試創(chuàng)建帖子
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 請求參數(shù)與 headers 設(shè)置
ApiService.java (擴(kuò)展)
/**
* 演示請求參數(shù)和 headers 設(shè)置
*/
public List<Post> getPostsByUserId(Long userId) {
// 設(shè)置請求參數(shù)和 headers
return restClient.get()
// 使用 uri 方法設(shè)置查詢參數(shù)
.uri(uriBuilder -> uriBuilder
.path("/posts")
.queryParam("userId", userId)
.build())
// 設(shè)置請求頭
.header("Accept", "application/json")
.header("Authorization", "Bearer token123")
.retrieve()
.body(Post[].class);
}3.2 攔截器使用
創(chuàng)建一個(gè)自定義攔截器,用于日志記錄:
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);
// 執(zhí)行請求
ClientHttpResponse response = execution.execute(request, body);
// 記錄響應(yīng)信息
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 (擴(kuò)展)
// 配置帶有攔截器的 RestClient
@Bean
public RestClient restClientWithInterceptor() {
return RestClient.builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.requestFactory(new JdkClientHttpRequestFactory())
// 添加自定義攔截器
.interceptors(new LoggingInterceptor())
.build();
}
3.3 錯(cuò)誤處理
ApiService.java (擴(kuò)展)
/**
* 演示高級錯(cuò)誤處理
*/
public User getUserWithErrorHandling(Long id) {
try {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
// 處理 4xx 客戶端錯(cuò)誤
.onStatus(HttpStatus::is4xxClientError, (request, response) -> {
String errorBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
throw new RuntimeException("Client error: " + response.getStatusCode() + ", Body: " + errorBody);
})
// 處理 5xx 服務(wù)器錯(cuò)誤
.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());
// 可以根據(jù)需要返回默認(rèn)值或重新拋出
return new User();
}
}3.4 超時(shí)設(shè)置
更新 RestClient 配置,添加超時(shí)設(shè)置:
RestClientConfig.java (擴(kuò)展)
// 配置帶有超時(shí)設(shè)置的 RestClient
@Bean
public RestClient restClientWithTimeout() {
// 創(chuàng)建 HTTP 客戶端工廠并設(shè)置超時(shí)
JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory();
// 設(shè)置連接超時(shí)
requestFactory.setConnectTimeout(Duration.ofSeconds(5));
// 設(shè)置讀取超時(shí)
requestFactory.setReadTimeout(Duration.ofSeconds(10));
return RestClient.builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.requestFactory(requestFactory)
.build();
}4. 實(shí)戰(zhàn)案例:RESTful API 客戶端實(shí)現(xiàn)
4.1 案例說明
我們將實(shí)現(xiàn)一個(gè)完整的 RESTful API 客戶端,用于管理"任務(wù)"資源,包括以下功能:
- 獲取所有任務(wù)
- 獲取單個(gè)任務(wù)
- 創(chuàng)建任務(wù)
- 更新任務(wù)
- 刪除任務(wù)
- 根據(jù)狀態(tài)篩選任務(wù)
4.2 數(shù)據(jù)模型
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 服務(wù)接口
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 服務(wù)實(shí)現(xiàn)
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;
// 任務(wù) API 的基礎(chǔ)路徑
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) {
// 設(shè)置創(chuàng)建時(shí)間
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) {
// 設(shè)置更新時(shí)間
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() {
// 創(chuàng)建任務(wù)
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());
// 獲取任務(wù)
Long taskId = createdTask.getId();
Task fetchedTask = taskService.getTaskById(taskId);
assertNotNull(fetchedTask);
assertEquals(taskId, fetchedTask.getId());
// 更新任務(wù)
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()));
// 按狀態(tài)篩選任務(wù)
List<Task> completedTasks = taskService.getTasksByStatus(true);
assertFalse(completedTasks.isEmpty());
// 刪除任務(wù)
assertDoesNotThrow(() -> taskService.deleteTask(taskId));
// 驗(yàn)證任務(wù)已刪除
assertThrows(RuntimeException.class, () -> taskService.getTaskById(taskId));
}
}5. 最佳實(shí)踐與性能優(yōu)化
5.1 最佳實(shí)踐
- 單一職責(zé)原則:每個(gè) RestClient 實(shí)例專注于特定的 API 或服務(wù)
- 異常處理:始終處理可能的異常,提供有意義的錯(cuò)誤信息
- 連接池管理:合理配置連接池大小和超時(shí)時(shí)間
- 避免重復(fù)創(chuàng)建:RestClient 實(shí)例應(yīng)該是單例的,避免頻繁創(chuàng)建和銷毀
- 日志記錄:使用攔截器記錄關(guān)鍵請求和響應(yīng)信息,便于調(diào)試
- 配置外部化:將基礎(chǔ) URL、超時(shí)時(shí)間等配置放在配置文件中
- 使用 Builders:利用 RestClient 的構(gòu)建器模式創(chuàng)建客戶端實(shí)例
5.2 性能優(yōu)化
- 連接池配置:
@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();
}- 響應(yīng)壓縮:啟用請求和響應(yīng)壓縮
@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();
}- 緩存策略:對于頻繁訪問且不常變化的資源,實(shí)現(xiàn)緩存機(jī)制
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(), () -> {
// 緩存未命中時(shí),從 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 基礎(chǔ) URL api.base-url=https://jsonplaceholder.typicode.com # 連接超時(shí)(毫秒) api.connection-timeout=5000 # 讀取超時(shí)(毫秒) 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();
}
}總結(jié)
本教程詳細(xì)介紹了 Spring Boot 3 中 RestClient 的使用方法,從基礎(chǔ)到高級,涵蓋了各種常見場景和最佳實(shí)踐。RestClient 作為 RestTemplate 的現(xiàn)代替代方案,提供了更簡潔、更靈活的 API,是開發(fā) RESTful 客戶端的理想選擇。
通過本教程,你應(yīng)該能夠:
- 理解 RestClient 的核心概念和優(yōu)勢
- 配置和使用 RestClient 發(fā)送各種 HTTP 請求
- 處理請求參數(shù)、headers 和響應(yīng)數(shù)據(jù)
- 使用攔截器、錯(cuò)誤處理等高級特性
- 實(shí)現(xiàn)完整的 RESTful API 客戶端
- 應(yīng)用最佳實(shí)踐和性能優(yōu)化技巧
RestClient 結(jié)合了 Spring 的強(qiáng)大功能和現(xiàn)代 Java 的特性,為開發(fā)者提供了高效、可靠的 HTTP 客戶端解決方案。在實(shí)際項(xiàng)目中,應(yīng)根據(jù)具體需求選擇合適的配置和功能,以獲得最佳的性能和可維護(hù)性。
到此這篇關(guān)于Spring Boot 3 RestClient使用實(shí)戰(zhàn)案例的文章就介紹到這了,更多相關(guān)SpringBoot RestClient使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT
這篇文章主要為大家詳細(xì)介紹了如何純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2026-02-02
如何在Maven項(xiàng)目中運(yùn)行JUnit5測試用例實(shí)現(xiàn)
這篇文章主要介紹了如何在Maven項(xiàng)目中運(yùn)行JUnit5測試用例實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Jmeter如何獲取jtl文件中所有的請求報(bào)文詳解
JMeter的可以創(chuàng)建一個(gè)包含測試運(yùn)行結(jié)果的文本文件,這些通常稱為JTL文件,因?yàn)檫@是默認(rèn)擴(kuò)展名,但可以使用任何擴(kuò)展名,這篇文章主要給大家介紹了關(guān)于Jmeter如何獲取jtl文件中所有的請求報(bào)文的相關(guān)資料,需要的朋友可以參考下2021-09-09
Springboot配置過濾器實(shí)現(xiàn)過程解析
這篇文章主要介紹了Springboot配置過濾器實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
java截取字符串中的指定字符的兩種方法(以base64圖片為例)
本文介紹了使用Java截取字符串中指定字符的方法,通過substring索引和正則實(shí)現(xiàn),文章詳細(xì)介紹了實(shí)現(xiàn)步驟和示例代碼,對于想要了解如何使用Java截取字符串指定字符的讀者具有一定的參考價(jià)值2023-08-08
SpringMVC攔截器實(shí)現(xiàn)登錄認(rèn)證
這篇文章主要介紹了SpringMVC攔截器實(shí)現(xiàn)登錄認(rèn)證的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
SpringBoot集成Sa-Token實(shí)現(xiàn)權(quán)限認(rèn)證流程入門教程
本文詳解SpringBoot集成Sa-Token框架的入門步驟,涵蓋依賴添加、配置設(shè)置、攔截器注冊、權(quán)限角色邏輯定義、設(shè)備工具類創(chuàng)建及登錄/注銷功能改造,通過注解實(shí)現(xiàn)鑒權(quán),解決權(quán)限認(rèn)證問題,感興趣的朋友跟隨小編一起看看吧2025-09-09

