最小 WebFlux 应用
Spring WebFlux 需要引入 spring-webflux:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
最小启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EventdApiApplication {
public static void main(String[] args) {
SpringApplication.run(EventdApiApplication.class, args);
}
}
Spring Boot WebFlux 默认使用 Netty 作为底层服务器(不是 Tomcat)。这意味着第 1–3 章讲的所有 Reactor Netty 概念(EventLoop、背压、写水位线)都在 WebFlux 底层生效。
@RestController 与路由映射
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public Flux<User> listUsers() {
return Flux.just(
new User(1L, "alice"),
new User(2L, "bob")
);
}
@GetMapping("/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return Mono.just(new User(id, "user-" + id));
}
@PostMapping
public Mono<User> createUser(@RequestBody Mono<User> user) {
return user.map(u -> new User(System.currentTimeMillis(), u.name()));
}
@DeleteMapping("/{id}")
public Mono<Void> deleteUser(@PathVariable Long id) {
System.out.println("Deleting user " + id);
return Mono.empty();
}
}
注意返回值类型:
Mono<T>—— 0 或 1 个元素的异步结果Flux<T>—— 0 到 N 个元素的异步流Mono<Void>—— 只有完成信号,没有数据
Spring 会自动把 User 对象序列化为 JSON(需要引入 jackson)。
路径变量与查询参数
@GetMapping("/search")
public Flux<User> search(
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "10") int limit,
@RequestParam(required = false) List<String> tags // ?tags=java&tags=spring
) {
return Flux.just(new User(1L, name != null ? name : "default"));
}
| 注解 | 来源 | 示例 |
|---|---|---|
@PathVariable | URL 路径段 | /users/{id} → @PathVariable Long id |
@RequestParam | 查询参数 | ?name=abc → @RequestParam String name |
@RequestHeader | 请求头 | X-Token: abc → @RequestHeader("X-Token") String token |
@CookieValue | Cookie | session=abc → @CookieValue("session") String session |
请求体:Mono vs 直接对象
WebFlux 中 @RequestBody 可以绑定为 Mono<T> 或 Flux<T>:
// 方式 A:Mono(惰性处理)
@PostMapping
public Mono<User> create(@RequestBody Mono<User> userMono) {
return userMono.flatMap(u -> validate(u))
.flatMap(u -> saveToDb(u));
}
// 方式 B:直接对象(Spring 先异步解码,再调用方法)
@PostMapping("/sync")
public Mono<User> createSync(@RequestBody User user) {
// 方法被调用时 user 已经是完整对象;解码过程本身不阻塞线程
return saveToDb(user);
}
两种方式都不会「阻塞聚合」:WebFlux 用响应式解码器把请求体读完,再调用 handler,期间不占用一个线程等待 I/O。区别在时机和形态——方式 A 把解码推迟到订阅 Mono 时,便于在解码后、业务前插入算子;方式 B 在进入方法前就已经解码完成,写法更直接,但拿不到解码阶段的控制点。都是合法用法,不要因为「响应式」就一律拒绝方式 B。
自定义 MediaType
@GetMapping(value = "/report", produces = MediaType.TEXT_PLAIN_VALUE)
public Mono<String> report() {
return Mono.just("Sales report content");
}
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<String> upload(@RequestPart("file") FilePart filePart) {
return filePart.content()
.map(dataBuffer -> {
try {
return dataBuffer.readableByteCount();
} finally {
// FilePart 的 DataBuffer 由调用方负责释放,漏掉会泄漏池化内存
DataBufferUtils.release(dataBuffer);
}
})
.reduce(0, Integer::sum)
.map(size -> "Uploaded " + size + " bytes");
}
produces 控制响应 Content-Type,consumes 控制接受的请求 Content-Type。
与 Reactor Netty 的关系
WebFlux 注解路由建立在 Reactor Netty 之上:
@RestController @GetMapping
↓ Spring DispatcherHandler
↓ RequestMappingHandlerMapping(匹配路由)
↓ InvocableHandlerMethod(调用方法)
↓ Mono/Flux 返回值
↓ ReactorHttpHandlerAdapter
↓ Reactor Netty HttpServer
所以第 1 章的背压、第 2 章的连接池、第 3 章的 WebSocket,在 WebFlux 应用中同样生效。区别只是 Spring 帮你封装了底层的 handle() 回调。
错误处理缺省行为
如果 controller 方法抛出异常,WebFlux 默认返回 500 和一个简单的错误 JSON:
{
"timestamp": "2026-11-05T06:12:00.000+00:00",
"path": "/api/users/999",
"status": 500,
"error": "Internal Server Error",
"requestId": "a1b2c3d4-5"
}
默认错误体包含 timestamp、path、status、error 和 requestId。注意 requestId 来自 ServerHttpRequest.getId(),是 WebFlux 为每个请求生成的内部 ID,不是业务层透传的 X-Request-ID;要把它们对齐得自己在过滤器里写入并覆盖错误属性。message 默认不会出现(server.error.include-message=never 的安全默认),只有开启后才会返回异常消息。第 5.3 节会讲如何自定义这个错误体并接入业务请求 ID。
小结
| 概念 | 注解/用法 |
|---|---|
| REST Controller | @RestController + @RequestMapping |
| 路由映射 | @GetMapping、@PostMapping 等 |
| 路径参数 | @PathVariable |
| 查询参数 | @RequestParam(支持 List、默认值) |
| 请求头 | @RequestHeader |
| 请求体 | @RequestBody Mono<T> 或 @RequestBody Flux<T> |
| 响应类型 | produces = MediaType... |
| 文件上传 | @RequestPart FilePart |
下一节 4.2 函数式路由 :RouterFunctions、RequestPredicates、与注解路由的对照和选择。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。