1. 简介

本文将带你掌握如何在 Spring Data 中使用响应式编程(Reactive Programming)操作 MongoDB,核心是 Reactive RepositoriesReactiveMongoTemplate 的配置与实战。

我们会覆盖以下几个关键组件的使用:

  • ReactiveCrudRepository:基础的响应式 CRUD 操作
  • ReactiveMongoRepository:增强版,支持 Query by Example
  • RxJava2CrudRepository:如果你偏好 RxJava 而非 Project Reactor
  • ReactiveMongoTemplate:更灵活的底层操作工具

⚠️ 注意:本文重点是“怎么用”,不是讲响应式编程原理。如果你还不熟悉 MonoFlux,建议先补一下 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:标识主键字段,类型可以是 StringObjectId

⚠️ 注意:id 字段建议用 String,避免引入 org.bson.types.ObjectId,简化序列化问题。


5. 响应式仓库使用

Spring Data Reactive 提供了几种仓库接口,用法和传统 Repository 高度一致,只是返回值变成了 MonoFlux

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


原始标题:Spring Data Reactive Repositories with MongoDB