🌊 Spring响应式编程从0到1实战教程
[分类:编程语言/Java]
完整代码驱动式教学 —— Mono/Flux、WebFlux、R2DBC、测试与最佳实践
📖 目录
- 第1章:环境准备
- 第2章:Reactor核心概念实战
- 第3章:构建响应式REST API
- 第4章:函数式路由
- 第5章:配置与数据库
- 第6章:错误处理与重试
- 第7章:测试
- 第8章:性能监控
- 第9章:生产环境最佳实践
- 第10章:完整项目结构
- 启动应用 & 测试API
📦 第1章:环境准备
1.1 开发环境要求
- JDK 11+ (推荐 JDK 17)
- Maven 3.6+ / Gradle 6+
- IntelliJ IDEA / VSCode
- Docker (可选, 用于数据库)
1.2 Maven依赖 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
</parent>
<dependencies>
<!-- WebFlux 响应式Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- R2DBC 响应式数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<!-- PostgreSQL驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</project>
⚡ 第2章:Reactor核心概念实战
2.1 Mono 和 Flux 基础
// Mono示例
Mono<String> mono = Mono.just("Hello Reactive");
mono.subscribe(
data -> System.out.println("收到: " + data),
error -> System.err.println("错误: " + error),
() -> System.out.println("完成")
);
// Flux示例
Flux<String> flux = Flux.just("A", "B", "C");
flux.subscribe(System.out::println);
// StepVerifier 测试
StepVerifier.create(Flux.range(1, 3))
.expectNext(1, 2, 3)
.verifyComplete();
2.2 常用操作符
// map 转换
Flux.range(1, 5).map(i -> i * 2); // 2,4,6,8,10
// filter 过滤
Flux.range(1, 5).filter(i -> i % 2 == 0); // 2,4
// flatMap 异步扁平化
Flux.just("hello", "world")
.flatMap(word -> Flux.fromArray(word.split("")));
// zip 合并
Flux.zip(Flux.just("A","B"), Flux.just(1,2))
.map(t -> t.getT1() + t.getT2()); // [A1, B2]
🏗️ 第3章:构建响应式REST API
3.1 实体类 User
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("users")
public class User {
@Id private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createdAt;
}
3.2 响应式Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Mono<User> findByUsername(String username);
Flux<User> findByAgeBetween(Integer min, Integer max);
@Query("SELECT * FROM users WHERE email LIKE $1")
Flux<User> findByEmailDomain(String domain);
}
3.3 服务层 UserService
@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {
private final UserRepository userRepository;
public Mono<User> createUser(User user) {
user.setCreatedAt(LocalDateTime.now());
return userRepository.save(user)
.doOnSuccess(u -> log.info("创建用户: {}", u));
}
public Mono<User> getUserById(Long id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new RuntimeException("用户不存在")));
}
public Flux<User> getAllUsers() {
return userRepository.findAll();
}
// 更新、删除等略...
}
3.4 控制器 UserController
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> createUser(@RequestBody User user) {
return userService.createUser(user);
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable Long id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@GetMapping
public Flux<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamUsers() {
return userService.getAllUsers().delayElements(Duration.ofSeconds(1));
}
}
🔄 第4章:函数式路由
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
return RouterFunctions.route()
.path("/api/users", builder -> builder
.GET("/", handler::getAllUsers)
.GET("/{id}", handler::getUserById)
.POST("/", handler::createUser)
.PUT("/{id}", handler::updateUser)
.DELETE("/{id}", handler::deleteUser)
)
.build();
}
}
@Component
@RequiredArgsConstructor
public class UserHandler {
private final UserService userService;
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
return ServerResponse.ok().body(userService.getAllUsers(), User.class);
}
public Mono<ServerResponse> getUserById(ServerRequest request) {
return userService.getUserById(Long.valueOf(request.pathVariable("id")))
.flatMap(user -> ServerResponse.ok().bodyValue(user))
.switchIfEmpty(ServerResponse.notFound().build());
}
// 其他方法类似...
}
🗄️ 第5章:配置与数据库
application.yml
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/reactive_db
username: postgres
password: password
pool:
initial-size: 5
max-size: 20
webflux:
base-path: /api
server:
port: 8080
netty:
connection-timeout: 2s
schema.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
age INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
⚠️ 第6章:错误处理与重试
全局异常处理
@Configuration
public class ExceptionConfig {
@Bean
@Order(-2)
public ErrorResponseExceptionHandler errorResponseExceptionHandler(
ErrorAttributes errorAttributes, ServerCodecConfigurer codecConfigurer) {
return new ErrorResponseExceptionHandler(errorAttributes, codecConfigurer);
}
}
// 自定义错误属性
static class ErrorResponseExceptionHandler extends AbstractErrorWebExceptionHandler {
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(request, options);
// 增强字段
errorAttributes.put("timestamp", System.currentTimeMillis());
return errorAttributes;
}
}
重试机制 (Resilience4j + Reactor)
// Reactor内置重试
Mono.fromCallable(this::unstableCall)
.retryWhen(RetrySpec.fixedDelay(3, Duration.ofSeconds(1))
.filter(throwable -> throwable instanceof RuntimeException));
// Resilience4j重试
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofSeconds(1))
.build();
Retry retry = RetryRegistry.of(config).retry("userService");
Mono.fromCallable(this::unstableCall)
.transformDeferred(RetryOperator.of(retry));
🧪 第7章:测试
7.1 控制器单元测试 (WebTestClient)
@ExtendWith(SpringExtension.class)
@WebFluxTest(UserController.class)
class UserControllerTest {
@Autowired WebTestClient webTestClient;
@MockBean UserService userService;
@Test
void testGetUserById() {
User user = new User(1L, "test", "test@ex.com", 25, null);
when(userService.getUserById(1L)).thenReturn(Mono.just(user));
webTestClient.get().uri("/api/users/1")
.exchange()
.expectStatus().isOk()
.expectBody(User.class).isEqualTo(user);
}
}
7.2 集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTest {
@Autowired WebTestClient webTestClient;
@Autowired R2dbcEntityTemplate entityTemplate;
@BeforeEach void clean() {
entityTemplate.delete(User.class).all().block();
}
@Test
void testCreateAndFind() {
User newUser = new User(null, "integ", "i@test.com", 30, null);
webTestClient.post().uri("/api/users").bodyValue(newUser)
.exchange().expectStatus().isCreated()
.expectBody().jsonPath("$.id").exists();
}
}
📊 第8章:性能监控
Actuator + Prometheus
<!-- 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yml 配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
自定义指标注解
@Aspect
@Component
public class MetricsAspect {
@Around("@annotation(timed)")
public Object measure(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
Timer.Sample sample = Timer.start(meterRegistry);
String method = pjp.getSignature().getName();
Object result = pjp.proceed();
if (result instanceof Mono) {
return ((Mono<?>) result).doOnSuccessOrError((v, t) ->
sample.stop(Timer.builder(timed.value())
.tag("method", method)
.tag("status", t == null ? "success" : "error")
.register(meterRegistry)));
}
// Flux类似...
return result;
}
}
🚀 第9章:生产环境最佳实践
熔断器配置 (Resilience4j)
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slowCallRateThreshold(50)
.slowCallDurationThreshold(Duration.ofSeconds(2))
.permittedNumberOfCallsInHalfOpenState(10)
.slidingWindowSize(100)
.minimumNumberOfCalls(10)
.waitDurationInOpenState(Duration.ofSeconds(30))
.build();
return CircuitBreakerRegistry.of(config);
}
public <T> Mono<T> withCircuitBreaker(Mono<T> publisher, String name) {
return publisher.transformDeferred(
CircuitBreakerOperator.of(circuitBreakerRegistry().circuitBreaker(name))
);
}
连接池调优
spring:
r2dbc:
pool:
initial-size: 10
max-size: 50
max-idle-time: 30m
max-life-time: 1h
validation-query: SELECT 1
webflux:
max-in-memory-size: 16MB
server:
netty:
max-connections: 10000
worker-threads: 4
📁 第10章:完整项目结构
reactive-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/example/reactivedemo/
│ │ │ ├── ReactiveDemoApplication.java
│ │ │ ├── config/
│ │ │ ├── controller/
│ │ │ ├── handler/
│ │ │ ├── model/
│ │ │ ├── repository/
│ │ │ ├── service/
│ │ │ ├── exception/
│ │ │ └── metrics/
│ │ └── resources/
│ │ ├── application.yml
│ │ └── schema.sql
│ └── test/
│ └── java/...
▶️ 启动应用 & 测试API
@SpringBootApplication
@EnableR2dbcRepositories
public class ReactiveDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ReactiveDemoApplication.class, args);
}
}
🌐 测试命令
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"username":"john","email":"john@ex.com","age":25}'
# 查询所有用户
curl http://localhost:8080/api/users
# 流式响应 (SSE)
curl http://localhost:8080/api/users/stream
# 更新
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"username":"john_new","email":"j@ex.com","age":26}'
# 删除
curl -X DELETE http://localhost:8080/api/users/1
📘 说明:确保 PostgreSQL 已启动并创建了 reactive_db 数据库,用户名密码与配置一致。
总结
本教程从环境准备开始,逐步介绍了Spring响应式编程的核心概念和实战应用,包括:
- Reactor核心库的Mono和Flux使用
- 响应式REST API的构建
- 函数式路由的实现
- R2DBC响应式数据库操作
- 错误处理与重试机制
- 测试策略与实践
- 性能监控与生产环境最佳实践
通过完整的代码示例和项目结构,帮助开发者快速上手Spring响应式编程,构建高性能、可伸缩的响应式应用。
💰 为什么选择 32ai?
低至 0.56 : 1 比率 🔗 快速访问: 点击访问 — 直连、无需魔法。
标签:Spring、响应式编程、WebFlux、R2DBC、Java
欢迎在评论区交流讨论!
原创声明:本文为原创教程,转载请注明出处