Spring Boot + WebFlux 全面使用指南

一、什么是 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 Web
    • Spring Data R2DBC(如需数据库)
    • H2 DatabaseMariaDB 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 风格一致

⚠️ 注意:方法必须返回 MonoFlux


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 的价值不在于"更快",而在于"更高吞吐、更低资源消耗"。在合适的场景下使用它,将显著提升系统伸缩性。

📚 官方文档:

相关推荐
陌殇殇5 小时前
001 Spring AI Alibaba框架整合百炼大模型平台 — 快速入门
人工智能·spring boot·ai
言慢行善5 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星5 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟5 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z5 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可5 小时前
Java 中的实现类是什么
java·开发语言
He少年5 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新5 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏4946 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构