Объедините два моно вместе, где второй моно подписан на первый - PullRequest
0 голосов
/ 11 февраля 2020

Недавно я изучал реактивное программирование с использованием библиотек реакторов в Java на основе среды Spring, и по большей части мне удалось справиться с этим. Однако я несколько раз оказывался в одной и той же ситуации и хотел бы получить совет о том, где я иду не так.

Суть того, с чем я борюсь, часто заключается в том, что я хочу сделать что-то с моно, например найти некоторые дополнительные данные, а затем добавить их обратно в исходный моно. Функция zip представляется мне идеальным кандидатом, но затем я в итоге подписываюсь на оригинальный моно дважды, что не является моим намерением.

Вот надуманный пример типа ситуации, которую я пытался решить, как Я не могу поделиться кодом своей компании. Предполагается, что мы используем библиотеку реактивных данных и настроили регистратор, а класс Person является неизменным, но имеет методы .

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, и когда я собираю их вместе, но я не хочу делать ненужные вызовы в хранилище, если я могу избежать этого.

Есть ли у кого-нибудь предложение для лучшего способа обработки такого рода поведения?

1 Ответ

1 голос
/ 12 февраля 2020

Как правило, вы не «добавляете данные» в 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);
}
...