我最近一直在使用带有Spring框架的Java中的反应堆库来学习反应式编程,在大多数情况下,我已经掌握了它。但是,我已经几次遇到相同的情况,并且想对我要去哪里做一些建议。
我一直在苦苦挣扎的要点通常是我想对单声道做一些事情,例如找到一些补充数据,然后将其添加回原始的单声道。 zip函数对我来说似乎是理想的候选者,但是我最终两次订阅了原始的mono,这不是我的意图。
这是我一直试图解决的情况类型的一个人为设计的示例,因为我无法共享我的公司代码。假设我们正在使用反应式数据库并设置了记录器,并且Person类是不可变的,但具有with
public Mono<Person> getPersonWithFamilyMembers(Integer id){
log.info("Finding person with id {}", id);
personRepository.findById(id)
.switchIfEmpty(Mono.error(NotFoundException::new))
.doOnNext(person -> log.info("Found person: {}", person))
.as(this::fetchAndAddFamilyMembers)
.doOnSuccess(person -> log.info("Successfully found person with family members"));
}
private Mono<Person> fetchAndAddFamilyMembers(Mono<Person> personMono){
Mono<List<Person>> familyMembersMono = personMono
.map(Person::getFamilyId)
.flatMapMany(PersonRepository::findByFamilyId)
.collectList();
return personMono.zipWith(familyMembersMono, Person::withFamilyMembers);
}
运行这样的代码时看到的输出是:
INFO | Finding person with id 1
INFO | Found person: Person(id=1, familyId=1, familyMembers=[])
INFO | Found person: Person(id=1, familyId=1, familyMembers=[])
INFO | Successfully found person with family members
这确实很有意义,因为原始人单声道已经在两个地方订阅了,我将其映射到familyMembersMono
,当我将它们压缩在一起时,但是如果我不想对存储库进行不必要的调用,可以避免。
有人建议使用更好的方法来处理这种行为吗?
通常,您不是将数据“添加”到Mono,而是将其中的数据“添加”。考虑到这一点,请使用flatMap
而不是as
:
public Mono<Person> getPersonWithFamilyMembers(Integer id){
log.info("Finding person with id {}", id);
return personRepository.findById(id)
.switchIfEmpty(Mono.error(NotFoundException::new))
.doOnNext(person -> log.info("Found person: {}", person))
.flatMap(this::fetchAndAddFamilyMembers)
.doOnSuccess(person -> log.info("Successfully found person with family members"));
}
private Mono<Person> fetchAndAddFamilyMembers(Person person){ // this accepts Person, not Mono<Person>
return personRepository.findByFamilyId(person.getFamilyId())
.collectList()
.map(person::withFamilyMembers);
}