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

📚 官方文档:

相关推荐
paopaokaka_luck8 小时前
基于springboot3+vue3的智能文库平台(AI智能搜索、AI智能汇总、实时在线状态展示、多格式文档预览与富文本编辑、Echarts图形化分析)
前端·网络·spring boot·网络协议·echarts
zzzll11119 小时前
Spring Boot 入门指南:从零开始构建 Java Web 应用
java·前端·spring boot
热心市民lcj14 小时前
Spring Boot 整合 Caffeine 本地缓存实战
spring boot·后端·缓存
天丁o15 小时前
Spring Boot + MyBatis Plus 考勤日报统计报表:打卡记录聚合、异常分类和明细下钻 Demo
spring boot·mybatis plus·企业数字化·考勤系统·报表统计
智_永无止境15 小时前
Spring Boot 集成 OnlyOffice
java·spring boot·后端
Devin~Y16 小时前
从本地生活电商到 AI RAG:互联网大厂 Java 面试场景完整实战
java·spring boot·redis·elasticsearch·spring cloud·kafka·rag
米码收割机16 小时前
SA508-3钢回火焊道焊接温度场数值模拟(模拟图+论文)
spring boot·express·宠物
米码收割机17 小时前
【项目】spring boot+vue3 宠物领养系统(源码+文档)【独一无二】
java·spring boot·宠物
momo1 天前
Java+WebAI黑马知识点
java·spring boot·mybatis
就改了1 天前
Mybatis快速入门大全(详细版)
java·spring boot·mybatis