1. 简介
本文将带你掌握如何在 Spring Data 中使用响应式编程(Reactive Programming)操作 MongoDB,核心是 Reactive Repositories 与 ReactiveMongoTemplate 的配置与实战。
我们会覆盖以下几个关键组件的使用:
- ✅
ReactiveCrudRepository
:基础的响应式 CRUD 操作 - ✅
ReactiveMongoRepository
:增强版,支持 Query by Example - ✅
RxJava2CrudRepository
:如果你偏好 RxJava 而非 Project Reactor - ✅
ReactiveMongoTemplate
:更灵活的底层操作工具
⚠️ 注意:本文重点是“怎么用”,不是讲响应式编程原理。如果你还不熟悉 Mono
和 Flux
,建议先补一下 Project Reactor 基础。
2. 环境准备
要启用 Reactive MongoDB,必须引入对应依赖。以下是 pom.xml
的关键配置:
<dependencies>
<!-- Spring Boot Reactive MongoDB 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<!-- 嵌入式 MongoDB,用于单元测试 -->
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
✅ 使用 spring-boot-starter-data-mongodb-reactive
是关键,它会自动配置响应式基础设施。
⚠️ 踩坑提醒:别错引入 spring-data-mongodb
这个非响应式版本,否则你会发现 ReactiveCrudRepository
根本不生效。
3. 配置说明
理论上,你需要手动启用响应式仓库支持:
@EnableReactiveMongoRepositories
public class MongoReactiveApplication extends AbstractReactiveMongoConfiguration {
@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}
@Override
protected String getDatabaseName() {
return "reactive";
}
}
但!如果你用的是 Spring Boot + 嵌入式 MongoDB(如本文示例),这套配置 完全不需要手写,Spring Boot 自动帮你搞定了。
✅ 实际开发中,你只需要在 application.yml
配置连接信息即可:
spring:
data:
mongodb:
uri: mongodb://localhost:27017/reactive
4. 定义 Document
先定义一个 MongoDB 的文档实体类 Account
:
@Document
public class Account {
@Id
private String id;
private String owner;
private Double value;
// 构造函数、getter、setter 省略
}
@Document
:声明这是一个 MongoDB 文档(对应关系型数据库的表)@Id
:标识主键字段,类型可以是String
或ObjectId
⚠️ 注意:id
字段建议用 String
,避免引入 org.bson.types.ObjectId
,简化序列化问题。
5. 响应式仓库使用
Spring Data Reactive 提供了几种仓库接口,用法和传统 Repository 高度一致,只是返回值变成了 Mono
或 Flux
。
5.1 ReactiveCrudRepository
这是最基础的响应式仓库接口,支持标准 CRUD 操作:
@Repository
public interface AccountCrudRepository
extends ReactiveCrudRepository<Account, String> {
Flux<Account> findAllByValue(Double value);
Mono<Account> findFirstByOwner(Mono<String> owner);
}
关键点:
- 方法返回值:
Mono<T>
表示单个结果,Flux<T>
表示多个结果 - 参数也可以是响应式类型,如
Mono<String>
,实现响应式参数传递 - 支持方法名解析(Method Query),Spring 自动解析
findAllByValue
生成查询
✅ 实际开发中,这种风格简洁直接,适合大多数场景。
5.2 ReactiveMongoRepository
继承自 ReactiveCrudRepository
,额外支持 **Query by Example (QBE)**,适合动态查询:
@Repository
public interface AccountReactiveRepository
extends ReactiveMongoRepository<Account, String> { }
使用示例:
Flux<Account> accountFlux = repository
.findAll(Example.of(new Account(null, "owner", null)));
这会查询所有 owner
为 "owner" 的记录,其他字段为 null
表示忽略。
✅ QBE 适合构建动态条件查询,避免拼接 Criteria。
⚠️ 注意:QBE 默认使用“精确匹配”,不支持模糊查询,如需模糊需自定义 ExampleMatcher
。
5.3 RxJava2CrudRepository
如果你项目中用的是 RxJava2 而不是 Project Reactor,可以用这个接口:
@Repository
public interface AccountRxJavaRepository
extends RxJava2CrudRepository<Account, String> {
Observable<Account> findAllByValue(Double value);
Single<Account> findFirstByOwner(Single<String> owner);
}
- 返回值:
Observable<T>
、Single<T>
、Maybe<T>
等 RxJava 类型 - 参数也可以是响应式类型
✅ 适合已使用 RxJava 的老项目迁移。
❌ 新项目建议统一使用 Project Reactor(Mono
/Flux
),避免技术栈混乱。
5.4 基础操作测试
使用 StepVerifier
验证响应式流行为,这是响应式测试的标配:
@Test
public void givenValue_whenFindAllByValue_thenFindAccount() {
repository.save(new Account(null, "Bill", 12.3)).block();
Flux<Account> accountFlux = repository.findAllByValue(12.3);
StepVerifier
.create(accountFlux)
.assertNext(account -> {
assertEquals("Bill", account.getOwner());
assertEquals(Double.valueOf(12.3) , account.getValue());
assertNotNull(account.getId());
})
.expectComplete()
.verify();
}
@Test
public void givenOwner_whenFindFirstByOwner_thenFindAccount() {
repository.save(new Account(null, "Bill", 12.3)).block();
Mono<Account> accountMono = repository
.findFirstByOwner(Mono.just("Bill"));
StepVerifier
.create(accountMono)
.assertNext(account -> {
assertEquals("Bill", account.getOwner());
assertEquals(Double.valueOf(12.3) , account.getValue());
assertNotNull(account.getId());
})
.expectComplete()
.verify();
}
@Test
public void givenAccount_whenSave_thenSaveAccount() {
Mono<Account> accountMono = repository.save(new Account(null, "Bill", 12.3));
StepVerifier
.create(accountMono)
.assertNext(account -> assertNotNull(account.getId()))
.expectComplete()
.verify();
}
⚠️ 踩坑提醒:测试中 .block()
是为了同步执行,生产环境**严禁滥用 block()**,会破坏响应式非阻塞优势。
6. ReactiveMongoTemplate
当 Repository 搞不定复杂查询时,ReactiveMongoTemplate
就派上用场了——它相当于响应式的 JdbcTemplate
。
6.1 配置 Bean
@Configuration
public class ReactiveMongoConfig {
@Autowired
MongoClient mongoClient;
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(mongoClient, "reactive");
}
}
注意:数据库名应与配置一致,本文使用 reactive
。
6.2 在 Service 中使用
@Service
public class AccountTemplateOperations {
@Autowired
ReactiveMongoTemplate template;
public Mono<Account> findById(String id) {
return template.findById(id, Account.class);
}
public Flux<Account> findAll() {
return template.findAll(Account.class);
}
public Mono<Account> save(Mono<Account> account) {
return template.save(account);
}
}
✅ ReactiveMongoTemplate
支持:
- 自定义查询(
query()
+Criteria
) - 聚合操作(
aggregate()
) - 更新、删除、upsert 等高级操作
建议:简单 CRUD 用 Repository,复杂逻辑用 Template,两者互补。
7. 总结
本文系统梳理了 Spring Data Reactive + MongoDB 的核心用法:
组件 | 适用场景 |
---|---|
ReactiveCrudRepository |
标准 CRUD,方法名查询 |
ReactiveMongoRepository |
支持 Query by Example |
RxJava2CrudRepository |
适配 RxJava 技术栈 |
ReactiveMongoTemplate |
复杂查询、聚合、动态条件 |
✅ 最佳实践建议:
- 新项目优先使用
ReactiveCrudRepository
+Mono
/Flux
- 测试务必用
StepVerifier
,别用block()
断言 - 生产环境避免
block()
,保持响应式链完整
完整示例代码已上传至 GitHub:https://github.com/yourname/spring-data-mongodb-reactive-demo