最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 3 RestClient使用實(shí)戰(zhàn)案例

 更新時(shí)間:2026年01月23日 10:49:47   作者:李昊哲小課  
本文詳細(xì)介紹了SpringBoot3中RestClient的使用方法,從基礎(chǔ)到高級,涵蓋了各種常見場景和最佳實(shí)踐,通過本教程,讀者可以理解RestClient的核心概念和優(yōu)勢,感興趣的朋友跟隨小編一起看看吧

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í)踐

  1. 單一職責(zé)原則:每個(gè) RestClient 實(shí)例專注于特定的 API 或服務(wù)
  2. 異常處理:始終處理可能的異常,提供有意義的錯(cuò)誤信息
  3. 連接池管理:合理配置連接池大小和超時(shí)時(shí)間
  4. 避免重復(fù)創(chuàng)建:RestClient 實(shí)例應(yīng)該是單例的,避免頻繁創(chuàng)建和銷毀
  5. 日志記錄:使用攔截器記錄關(guān)鍵請求和響應(yīng)信息,便于調(diào)試
  6. 配置外部化:將基礎(chǔ) URL、超時(shí)時(shí)間等配置放在配置文件中
  7. 使用 Builders:利用 RestClient 的構(gòu)建器模式創(chuàng)建客戶端實(shí)例

5.2 性能優(yōu)化

  1. 連接池配置
@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();
}
  1. 響應(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();
}
  1. 異步請求:對于非阻塞場景,使用異步請求
@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();
}
  1. 緩存策略:對于頻繁訪問且不常變化的資源,實(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 null轉(zhuǎn)換為字符串的三種方法

    java null轉(zhuǎn)換為字符串的三種方法

    在Java開發(fā)中,正確處理null值至關(guān)重要,以避免空指針異常,本文介紹了三種常見的null值轉(zhuǎn)字符串方法:三元運(yùn)算符、Objects.toString方法、String.valueOf方法,感興趣的可以了解一下
    2024-10-10
  • java項(xiàng)目中的多線程實(shí)踐記錄

    java項(xiàng)目中的多線程實(shí)踐記錄

    項(xiàng)目開發(fā)中對于一些數(shù)據(jù)的處理需要用到多線程,比如文件的批量上傳,數(shù)據(jù)庫的分批寫入,大文件的分段下載等,主要涉及到多線程的一些知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下
    2021-11-11
  • 純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT

    純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)

    這篇文章主要介紹了如何在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如何獲取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)過程解析

    這篇文章主要介紹了Springboot配置過濾器實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • java截取字符串中的指定字符的兩種方法(以base64圖片為例)

    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)證

    這篇文章主要介紹了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í)現(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
  • SpringBoot解決跨域的五種方式

    SpringBoot解決跨域的五種方式

    在?Spring?Boot?中解決跨域(CORS,Cross-Origin?Resource?Sharing)問題有?多種方式,適用于不同場景,下面系統(tǒng)地介紹?5?種主流方法,從簡單到靈活,附代碼示例和適用場景,需要的朋友可以參考下
    2025-10-10

最新評論

宝鸡市| 洪洞县| 永新县| 县级市| 锡林郭勒盟| 从化市| 仪征市| 革吉县| 大余县| 黑水县| 垦利县| 海丰县| 卢氏县| 乌恰县| 安岳县| 阿鲁科尔沁旗| 永寿县| 繁昌县| 全南县| 会昌县| 定远县| 板桥市| 聊城市| 桐梓县| 康马县| 铜陵市| 仁化县| 通许县| 沐川县| 阿瓦提县| 崇阳县| 渝北区| 东方市| 广昌县| 新乡县| 郎溪县| 克东县| 克什克腾旗| 高唐县| 九江市| 蓝山县|