Spring WebFlux 2025 構建高性能非阻塞API
在現代分布式系統中,高并發場景越來越普遍,傳統的阻塞式IO模型已難以滿足需求。Spring WebFlux作為Spring生態中的響應式編程解決方案,經過多年發展已成為構建高性能非阻塞API的首選框架。本文將基于2025年最新技術棧,提供一套完整的Spring WebFlux實操指南。
技術棧準備
我們將使用以下最新技術組件:
- Spring Boot 3.3.0(包含Spring WebFlux 6.2.0)
- Reactor Core 3.6.0(響應式編程基礎)
- Spring Data R2DBC 3.3.0(關系型數據庫響應式訪問)
- H2 Database 2.3.0(嵌入式數據庫,方便演示)
- Project Reactor Addons 3.6.0(響應式工具類)
- Java 21(提供虛擬線程等新特性)
環境搭建
首先創建一個Spring Boot項目,推薦使用Spring Initializr(https://start.spring.io/),選擇以下依賴:
- Spring Reactive Web
- Spring Data R2DBC
- H2 Database
- Lombok(可選,簡化代碼)
Maven依賴文件(pom.xml)關鍵部分如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring WebFlux 核心依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 響應式數據庫支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<!-- H2 數據庫 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 響應式工具類 -->
<dependency>
<groupId>io.projectreactor.addons</groupId>
<artifactId>reactor-extra</artifactId>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
項目結構設計
我們將構建一個簡單的用戶管理API,采用分層架構:
com.example.reactive
├── config # 配置類
├── controller # 控制器層
├── handler # 函數式處理器
├── router # 路由配置
├── model # 數據模型
├── repository # 數據訪問層
└── service # 業務邏輯層
核心代碼實現
1. 數據模型
首先創建用戶實體類:
package com.example.reactive.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("users")
public class User {
@Id
private Long id;
private String username;
private String email;
private String fullName;
private boolean active;
}
2. 數據庫配置
創建R2DBC配置類,配置數據庫連接:
package com.example.reactive.config;
import io.r2dbc.h2.H2ConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
@Configuration
@EnableR2dbcRepositories(basePackages = "com.example.reactive.repository")
public class R2DBCConfig extends AbstractR2dbcConfiguration {
@Bean
@Override
public ConnectionFactory connectionFactory() {
// 使用內存模式的H2數據庫
return H2ConnectionFactory.inMemory("testdb");
}
}
創建數據庫初始化腳本(src/main/resources/schema.sql):
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
full_name VARCHAR(100) NOT NULL,
active BOOLEAN DEFAULT TRUE
);
3. 數據訪問層
創建響應式Repository接口:
package com.example.reactive.repository;
import com.example.reactive.model.User;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface UserRepository extends R2dbcRepository<User, Long> {
// 根據用戶名查詢用戶
Mono<User> findByUsername(String username);
// 根據郵箱查詢用戶
Mono<User> findByEmail(String email);
}
4. 業務邏輯層
創建服務接口和實現類:
package com.example.reactive.service;
import com.example.reactive.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface UserService {
Flux<User> findAll();
Mono<User> findById(Long id);
Mono<User> save(User user);
Mono<User> update(Long id, User user);
Mono<Void> deleteById(Long id);
Mono<User> findByUsername(String username);
}
實現類:
package com.example.reactive.service;
import com.example.reactive.model.User;
import com.example.reactive.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Override
public Flux<User> findAll() {
return userRepository.findAll();
}
@Override
public Mono<User> findById(Long id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new RuntimeException("User not found with id: " + id)));
}
@Override
public Mono<User> save(User user) {
// 可以在這里添加業務驗證邏輯
return userRepository.save(user);
}
@Override
public Mono<User> update(Long id, User user) {
return userRepository.findById(id)
.flatMap(existingUser -> {
existingUser.setUsername(user.getUsername());
existingUser.setEmail(user.getEmail());
existingUser.setFullName(user.getFullName());
existingUser.setActive(user.isActive());
return userRepository.save(existingUser);
})
.switchIfEmpty(Mono.error(new RuntimeException("User not found with id: " + id)));
}
@Override
public Mono<Void> deleteById(Long id) {
return userRepository.existsById(id)
.flatMap(exists -> {
if (exists) {
return userRepository.deleteById(id);
} else {
return Mono.error(new RuntimeException("User not found with id: " + id));
}
});
}
@Override
public Mono<User> findByUsername(String username) {
return userRepository.findByUsername(username)
.switchIfEmpty(Mono.error(new RuntimeException("User not found with username: " + username)));
}
}
5. API實現(注解式控制器)
創建基于注解的REST控制器:
package com.example.reactive.controller;
import com.example.reactive.model.User;
import com.example.reactive.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public Flux<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@GetMapping("/username/{username}")
public Mono<ResponseEntity<User>> getUserByUsername(@PathVariable String username) {
return userService.findByUsername(username)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> createUser(@RequestBody User user) {
return userService.save(user);
}
@PutMapping("/{id}")
public Mono<ResponseEntity<User>> updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteUser(@PathVariable Long id) {
return userService.deleteById(id)
.then(Mono.just(ResponseEntity.noContent().build()))
.onErrorResume(e -> Mono.just(ResponseEntity.notFound().build()));
}
// 全局異常處理
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException ex) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
}
6. API實現(函數式端點)
除了注解式控制器,Spring WebFlux還支持函數式編程模型。下面實現一套相同功能的函數式端點:
首先創建處理器:
package com.example.reactive.handler;
import com.example.reactive.model.User;
import com.example.reactive.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
public class UserHandler {
private final UserService userService;
// 獲取所有用戶
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(userService.findAll(), User.class);
}
// 根據ID獲取用戶
public Mono<ServerResponse> getUserById(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
return userService.findById(id)
.flatMap(user -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(user))
.switchIfEmpty(ServerResponse.notFound().build());
}
// 創建用戶
public Mono<ServerResponse> createUser(ServerRequest request) {
Mono<User> userMono = request.bodyToMono(User.class);
return userMono
.flatMap(user -> userService.save(user))
.flatMap(savedUser -> ServerResponse.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(savedUser));
}
// 更新用戶
public Mono<ServerResponse> updateUser(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
Mono<User> userMono = request.bodyToMono(User.class);
return userMono
.flatMap(user -> userService.update(id, user))
.flatMap(updatedUser -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(updatedUser))
.switchIfEmpty(ServerResponse.notFound().build());
}
// 刪除用戶
public Mono<ServerResponse> deleteUser(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
return userService.deleteById(id)
.then(ServerResponse.noContent().build())
.onErrorResume(e -> ServerResponse.notFound().build());
}
}
然后創建路由配置:
package com.example.reactive.router;
import com.example.reactive.handler.UserHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
@Configuration
public class UserRouter {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler userHandler) {
return RouterFunctions
.route(GET("/func/users"), userHandler::getAllUsers)
.andRoute(GET("/func/users/{id}"), userHandler::getUserById)
.andRoute(POST("/func/users"), userHandler::createUser)
.andRoute(PUT("/func/users/{id}"), userHandler::updateUser)
.andRoute(DELETE("/func/users/{id}"), userHandler::deleteUser);
}
}
響應式客戶端使用
Spring WebFlux提供了WebClient作為響應式HTTP客戶端,下面演示如何使用它:
package com.example.reactive.service;
import com.example.reactive.model.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
public class UserWebClient {
private final WebClient webClient;
// 構造函數注入WebClient
public UserWebClient() {
this.webClient = WebClient.create("http://localhost:8080");
}
// 獲取所有用戶
public Flux<User> getAllUsers() {
return webClient.get()
.uri("/api/users")
.retrieve()
.bodyToFlux(User.class);
}
// 根據ID獲取用戶
public Mono<User> getUserById(Long id) {
return webClient.get()
.uri("/api/users/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
// 創建用戶
public Mono<User> createUser(User user) {
return webClient.post()
.uri("/api/users")
.bodyValue(user)
.retrieve()
.bodyToMono(User.class);
}
}
測試響應式API
使用JUnit 5和Reactor Test進行測試:
package com.example.reactive.controller;
import com.example.reactive.model.User;
import com.example.reactive.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@WebFluxTest(UserController.class)
public class UserControllerTest {
@Autowired
private WebTestClient webTestClient;
@MockBean
private UserService userService;
@Test
public void testGetAllUsers() {
User user1 = new User(1L, "user1", "user1@example.com", "User One", true);
User user2 = new User(2L, "user2", "user2@example.com", "User Two", true);
when(userService.findAll()).thenReturn(Flux.just(user1, user2));
webTestClient.get().uri("/api/users")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBodyList(User.class)
.hasSize(2)
.contains(user1, user2);
}
@Test
public void testCreateUser() {
User user = new User(null, "newuser", "newuser@example.com", "New User", true);
User savedUser = new User(3L, "newuser", "newuser@example.com", "New User", true);
when(userService.save(any(User.class))).thenReturn(Mono.just(savedUser));
webTestClient.post().uri("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(user)
.exchange()
.expectStatus().isCreated()
.expectBody(User.class)
.isEqualTo(savedUser);
}
}
性能優化建議
-
背壓管理:利用Reactor的背壓機制,控制數據流速度,防止下游組件被壓垮
-
連接池配置:優化R2DBC連接池和WebClient連接池
@Bean public ConnectionFactory connectionFactory() { H2ConnectionConfiguration config = H2ConnectionConfiguration.builder() .url("r2dbc:h2:mem:testdb") .username("sa") .password("") .build(); return ConnectionPoolConfiguration.builder() .connectionFactory(new H2ConnectionFactory(config)) .maxSize(10) .build(); } -
數據批量處理:使用
Flux.buffer()或Flux.window()進行批量操作 -
緩存熱點數據:結合
cache()操作符緩存頻繁訪問的數據 -
監控與指標:集成Micrometer監控響應式流的性能指標
總結
Spring WebFlux提供了構建非阻塞、高性能API的完整解決方案。通過本文的實操指南,你已經了解了如何使用最新的Spring WebFlux技術棧構建響應式API,包括:
- 環境搭建與配置
- 響應式數據訪問
- 兩種API實現方式(注解式和函數式)
- 響應式客戶端使用
- 測試與性能優化
在實際項目中,應根據具體場景選擇合適的編程模型,并充分利用響應式編程的特性來提升系統的并發處理能力和資源利用率。隨著Java和Spring生態的不斷發展,響應式編程將會在高并發場景中發揮越來越重要的作用。

浙公網安備 33010602011771號