一、什么是 Spring WebFlux?
- 定位 :Spring Framework 5+ 提供的 响应式 Web 框架,与 Spring MVC 并列;
- 核心目标 :支持 非阻塞、异步、事件驱动 的高并发 Web 应用;
- 底层依赖 :
- 响应式流规范(Reactive Streams)
- Project Reactor(
Mono/Flux) - 非阻塞服务器(默认 Netty,也支持 Undertow、Servlet 3.1+ 容器)
✅ WebFlux ≠ WebMVC 替代品,而是 互补技术栈,适用于不同场景。
二、何时使用 WebFlux?
| 场景 | 推荐 |
|---|---|
| 高并发 I/O 密集型(API 网关、实时推送、IoT) | ✅ 强烈推荐 |
| 全链路响应式技术栈(R2DBC + WebClient + Reactive MQ) | ✅ |
| 低并发传统业务系统(后台管理、简单 CRUD) | ❌ 用 WebMVC 更简单 |
| 强事务性/复杂 SQL(需 Hibernate/JPA) | ❌ 不适合 |
三、快速入门:创建 WebFlux 项目
1. 使用 Spring Initializr(https://start.spring.io/)
选择:
- Project: Maven / Gradle
- Language: Java
- Spring Boot: 3.x(推荐)
- Dependencies :
Spring Reactive WebSpring Data R2DBC(如需数据库)H2 Database或MariaDB Driver(根据需要)
2. 手动添加依赖(Maven)
xml
<dependencies>
<!-- WebFlux 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 响应式数据库(以 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. 启动类(无需特殊注解)
java
@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
java
@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();
}
// 创建用户
@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);
}
}
✅ 优点:学习成本低,与 MVC 风格一致
⚠️ 注意:方法必须返回 Mono 或 Flux
B. 函数式(Functional)--- 纯函数式路由
1. 定义 Handler
java
@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)
java
@Configuration
public class UserRouter {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
return route()
.GET("/users/{id}", handler::getUser)
.GET("/users", handler::getAllUsers)
.build();
}
}
✅ 优点:更符合响应式思想,易于单元测试,无反射开销
💡 适合构建轻量级、高内聚的 API
五、响应式数据访问(R2DBC)
1. 实体类
java
@Table("users")
public class User {
@Id
private Long id;
private String name;
private String email;
// constructors, getters, setters
}
2. Repository
java
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByEmail(String email);
}
3. application.yml 配置
yaml
spring:
r2dbc:
url: r2dbc:mariadb://localhost:3306/mydb
username: root
password: password
支持连接池(需引入
io.r2dbc:r2dbc-pool)
六、响应式 HTTP 客户端:WebClient
替代 RestTemplate,非阻塞调用外部服务:
java
@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()); // 错误降级
}
}
七、全局异常处理
java
@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"));
}
}
也可使用函数式方式注册 WebExceptionHandler。
八、测试:WebTestClient
java
@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"));
}
}
九、性能与配置优化
1. 调整 Netty 参数(application.yml)
yaml
server:
netty:
connection-timeout: 30s
max-in-memory-size: 10MB # 防止 OOM
2. 启用背压控制
java
Flux.range(1, 1000)
.limitRate(100) // 控制上游发射速率
.onBackpressureBuffer(500); // 缓冲溢出数据
3. 监控与指标
集成 Micrometer + Prometheus,监控 reactor.netty.connection.provider.active.connections 等指标。
十、常见陷阱与最佳实践
| 问题 | 建议 |
|---|---|
| 在 WebFlux 中调用 JDBC / Thread.sleep() | ❌ 会阻塞 EventLoop,导致服务不可用 |
| 混合使用 WebMVC 和 WebFlux | ⚠️ 可以共存,但不要在同一个 Controller 中混用 |
| 忽略背压 | ⚠️ 大流量下可能 OOM,务必使用 limitRate / onBackpressureXXX |
过度使用 block() |
❌ 破坏响应式模型,仅用于测试或边界转换 |
✅ 总结:WebFlux 开发 Checklist
- 使用
spring-boot-starter-webflux - 返回类型为
Mono<T>或Flux<T> - 数据库使用 R2DBC(非 JDBC)
- HTTP 调用使用
WebClient - 避免任何阻塞操作
- 使用
WebTestClient测试 - 合理处理背压和错误
通过以上指南,你已掌握 Spring Boot + WebFlux 的完整开发能力 。记住:WebFlux 的价值不在于"更快",而在于"更高吞吐、更低资源消耗"。在合适的场景下使用它,将显著提升系统伸缩性。
📚 官方文档: