Spring Boot + WebFlux 全面使用實踐指南
一、什么是 Spring WebFlux?
- 定位:Spring Framework 5+ 提供的 響應式 Web 框架,與 Spring MVC 并列;
- 核心目標:支持 非阻塞、異步、事件驅(qū)動 的高并發(fā) Web 應用;
- 底層依賴:
- 響應式流規(guī)范(Reactive Streams)
- Project Reactor(
Mono/Flux) - 非阻塞服務器(默認 Netty,也支持 Undertow、Servlet 3.1+ 容器)
? WebFlux ≠ WebMVC 替代品,而是 互補技術(shù)棧,適用于不同場景。
二、何時使用 WebFlux?
| 場景 | 推薦 |
|---|---|
| 高并發(fā) I/O 密集型(API 網(wǎng)關(guān)、實時推送、IoT) | ? 強烈推薦 |
| 全鏈路響應式技術(shù)棧(R2DBC + WebClient + Reactive MQ) | ? |
| 低并發(fā)傳統(tǒng)業(yè)務系統(tǒng)(后臺管理、簡單 CRUD) | ? 用 WebMVC 更簡單 |
| 強事務性/復雜 SQL(需 Hibernate/JPA) | ? 不適合 |
三、快速入門:創(chuàng)建 WebFlux 項目
1. 使用 Spring Initializr(https://start.spring.io/)
選擇:
- Project: Maven / Gradle
- Language: Java
- Spring Boot: 3.x(推薦)
- Dependencies:
Spring Reactive WebSpring Data R2DBC(如需數(shù)據(jù)庫)H2 Database或MariaDB Driver(根據(jù)需要)
2. 手動添加依賴(Maven)
<dependencies>
<!-- WebFlux 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 響應式數(shù)據(jù)庫(以 MariaDB 為例) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb</groupId>
<artifactId>r2dbc-mariadb</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.mariadb</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>3. 啟動類(無需特殊注解)
@SpringBootApplication
public class WebfluxDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxDemoApplication.class, args);
}
}?? 啟動日志將顯示:
Netty started on port 8080
四、兩種編程模型
A. 注解式(Annotation-based)— 類似 Spring MVC
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
// 返回單個對象
@GetMapping("/{id}")
public Mono<User> getUser(@PathVariable String id) {
return userRepository.findById(id);
}
// 返回列表流
@GetMapping
public Flux<User> getAllUsers() {
return userRepository.findAll();
}
// 創(chuàng)建用戶
@PostMapping
public Mono<User> createUser(@RequestBody User user) {
return userRepository.save(user);
}
// SSE:服務器推送事件
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> "Event #" + seq);
}
}? 優(yōu)點:學習成本低,與 MVC 風格一致
?? 注意:方法必須返回 Mono 或 Flux
B. 函數(shù)式(Functional)— 純函數(shù)式路由
1. 定義 Handler
@Component
public class UserHandler {
private final UserRepository userRepository;
public UserHandler(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Mono<ServerResponse> getUser(ServerRequest request) {
String id = request.pathVariable("id");
Mono<User> user = userRepository.findById(id);
return user
.flatMap(u -> ServerResponse.ok().bodyValue(u))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
Flux<User> users = userRepository.findAll();
return ServerResponse.ok().body(users, User.class);
}
}2. 定義 RouterFunction(替代 @RequestMapping)
@Configuration
public class UserRouter {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
return route()
.GET("/users/{id}", handler::getUser)
.GET("/users", handler::getAllUsers)
.build();
}
}? 優(yōu)點:更符合響應式思想,易于單元測試,無反射開銷
?? 適合構(gòu)建輕量級、高內(nèi)聚的 API
五、響應式數(shù)據(jù)訪問(R2DBC)
1. 實體類
@Table("users")
public class User {
@Id
private Long id;
private String name;
private String email;
// constructors, getters, setters
}
2. Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByEmail(String email);
}
3. application.yml 配置
spring:
r2dbc:
url: r2dbc:mariadb://localhost:3306/mydb
username: root
password: password支持連接池(需引入
io.r2dbc:r2dbc-pool)
六、響應式 HTTP 客戶端:WebClient
替代 RestTemplate,非阻塞調(diào)用外部服務:
@Service
public class ExternalServiceClient {
private final WebClient webClient;
public ExternalServiceClient() {
this.webClient = WebClient.builder()
.baseUrl("https://api.example.com")
.build();
}
public Mono<UserProfile> fetchProfile(String userId) {
return webClient.get()
.uri("/profiles/{id}", userId)
.retrieve()
.bodyToMono(UserProfile.class)
.onErrorResume(e -> Mono.empty()); // 錯誤降級
}
}七、全局異常處理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public Mono<ResponseEntity<String>> handleUserNotFound(Exception ex) {
return Mono.just(ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("User not found"));
}
@ExceptionHandler(Exception.class)
public Mono<ResponseEntity<String>> handleGeneral(Exception ex) {
return Mono.just(ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Internal error"));
}
}也可使用函數(shù)式方式注冊 WebExceptionHandler。
八、測試:WebTestClient
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {
@Autowired
private WebTestClient webClient;
@Test
void shouldGetUser() {
webClient.get().uri("/users/1")
.exchange()
.expectStatus().isOk()
.expectBody(User.class)
.value(user -> assertThat(user.getName()).isEqualTo("Alice"));
}
}九、性能與配置優(yōu)化
1. 調(diào)整 Netty 參數(shù)(application.yml)
server:
netty:
connection-timeout: 30s
max-in-memory-size: 10MB # 防止 OOM2. 啟用背壓控制
Flux.range(1, 1000)
.limitRate(100) // 控制上游發(fā)射速率
.onBackpressureBuffer(500); // 緩沖溢出數(shù)據(jù)3. 監(jiān)控與指標
集成 Micrometer + Prometheus,監(jiān)控 reactor.netty.connection.provider.active.connections 等指標。
十、常見陷阱與最佳實踐
| 問題 | 建議 |
|---|---|
| 在 WebFlux 中調(diào)用 JDBC / Thread.sleep() | ? 會阻塞 EventLoop,導致服務不可用 |
| 混合使用 WebMVC 和 WebFlux | ?? 可以共存,但不要在同一個 Controller 中混用 |
| 忽略背壓 | ?? 大流量下可能 OOM,務必使用 limitRate / onBackpressureXXX |
過度使用 block() | ? 破壞響應式模型,僅用于測試或邊界轉(zhuǎn)換 |
? 總結(jié):WebFlux 開發(fā) Checklist
- 使用
spring-boot-starter-webflux - 返回類型為
Mono<T>或Flux<T> - 數(shù)據(jù)庫使用 R2DBC(非 JDBC)
- HTTP 調(diào)用使用
WebClient - 避免任何阻塞操作
- 使用
WebTestClient測試 - 合理處理背壓和錯誤
通過以上指南,你已掌握 Spring Boot + WebFlux 的完整開發(fā)能力。記?。?strong>WebFlux 的價值不在于“更快”,而在于“更高吞吐、更低資源消耗”。在合適的場景下使用它,將顯著提升系統(tǒng)伸縮性。
官方文檔:
- https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html
- https://r2dbc.io/
- https://projectreactor.io/docs
到此這篇關(guān)于Spring Boot + WebFlux 全面使用實踐指南的文章就介紹到這了,更多相關(guān)Spring Boot WebFlux使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot3中Spring?WebFlux?SSE服務器發(fā)送事件的實現(xiàn)步驟
- SpringBoot3 Spring WebFlux簡介(推薦)
- SpringBoot深入分析webmvc和webflux的區(qū)別
- springboot webflux 過濾器(使用RouterFunction實現(xiàn))
- SpringBoot之webflux全面解析
- SpringBoot?Webflux創(chuàng)建TCP/UDP?server并使用handler解析數(shù)據(jù)
- 關(guān)于springboot響應式編程整合webFlux的問題
- Springboot WebFlux集成Spring Security實現(xiàn)JWT認證的示例
- SpringBoot2使用WebFlux函數(shù)式編程的方法
相關(guān)文章
Spring Boot 中 @Scheduled 定時任務不生效的原因及解決方法
SpringBoot中@Scheduled注解用于創(chuàng)建定時任務,但有時任務可能不生效,本文介紹Spring Boot 中 @Scheduled 定時任務不生效的原因及解決方法,感興趣的朋友跟隨小編一起看看吧2025-11-11
Java線程編程中isAlive()和join()的使用詳解
這篇文章主要介紹了Java線程編程中isAlive()和join()的使用詳解,是Java入門學習中的基礎知識,需要的朋友可以參考下2015-09-09
解決StringBuffer和StringBuilder的擴容問題
這篇文章主要介紹了解決StringBuffer和StringBuilder的擴容問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Spring IOC簡單理解及創(chuàng)建對象的方式
這篇文章主要介紹了Spring IOC簡單理解及創(chuàng)建對象的方式,本文通過兩種方式給大家介紹創(chuàng)建對象的方法,通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2021-09-09
關(guān)于SpringBoot整合redis使用Lettuce客戶端超時問題
使用到Lettuce連接redis,一段時間后不操作,再去操作redis,會報連接超時錯誤,在其重連后又可使用,糾結(jié)是什么原因?qū)е碌哪兀旅嫘【幗o大家?guī)砹薙pringBoot整合redis使用Lettuce客戶端超時問題及解決方案,一起看看吧2021-08-08

