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

📚 官方文档:

相关推荐
Halo_tjn2 小时前
基于Java的相关知识点
java·开发语言·windows·python·算法
Cosolar2 小时前
Java 后端访问 https接口报 SSLHandshakeException 你遇到过吗
java·后端·面试
组合缺一2 小时前
带来 AI Agent 开发,OpenSolon v3.8.3 发布
java·人工智能·ai·langchain·llm·solon
ghostmen2 小时前
SpringBoot + Vue 实现 Python 在线调试器 - 技术方案文档
java·python·vue·springboot
阿蒙Amon2 小时前
C#每日面试题-简述匿名方法
java·面试·c#
山峰哥2 小时前
JOIN - 多表关联的魔法——3000字实战指南
java·大数据·开发语言·数据库·sql·编辑器
jghhh012 小时前
C#中实现不同进程(EXE)间通信的方案
java·单例模式·c#
m0_748252382 小时前
Foundation 表格的基本用法
开发语言·后端·rust
Mr.朱鹏2 小时前
Spring Boot 配置文件加载顺序与优先级详解
java·spring boot·后端·spring·maven·配置文件·yml