Исключение «Неудовлетворенная зависимость» при возврате Mono из springframework.data.repository.reactive.ReactiveCrudRepository во время разбиения на страницы - PullRequest
0 голосов
/ 25 марта 2020

Контекст: я успешно создал метод с параметром «org.springframework.data.domain.Pageable», предназначенный для возврата потока. Я нашел только статьи, в которых указывалось возвращать Flux вместо Mono при включении Pageable. Пока все хорошо.

Личное знание / предположение: нет смысла возвращать Flux, если это просто страница. В другом мире это не поток, возвращающий более одного результата. Что ж, если бы мне потребовалось от 2 до 10 страниц из 100, я бы увидел пункт о запрете использования Mono. Насколько я понимаю, реальное событие от retrieveAllPaged ниже - это либо 0 событие, либо 1 (никогда не более 1).

Вот метод, работающий с возвратом потока:

public interface ModelRepository extends ReactiveCrudRepository<Extrato, String> {
    @Query("{ id: { $exists: true }}")
    Flux<Extrato> retrieveAllFluxPaged(final Pageable page);
}

Предварительно : поскольку он никогда не вернет больше 1, я попытался:

public interface ExtratoRepository extends ReactiveCrudRepository<Extrato, String> {

    @Query("{ id: { $exists: true }}")
    Mono<Extrato> retrieveAllMonoPaged(final Pageable page);
}

И я получил исключение, которое, кажется, советует, что Mono не приемлемо:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testDataLoader' defined in file .... Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: public abstract reactor.core.publisher.Mono 

Моя проблема и прямая вопрос: не реально ли вернуть моно при разбивке на страницы? Если так, соответствующий комментарий будет таким: почему, если я позвоню второй раз из внешнего интерфейса, это будет полный новый поиск? Я имею в виду, например, что двойной вызов из Rx Js никогда не вызовет повторного использования потока, созданного первым вызовом, насколько я понимаю.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable()
export class AppService {

  constructor(private http: HttpClient) {}

  getPage_1_and_2() {

    this.http
      .get<any[]>('http://localhost:8080/extrato/paged?page=1&size=1')
      .pipe(map(data => data));

    this.http
      .get<any[]>('http://localhost:8080/extrato/paged?page=2&size=1')
      .pipe(map(data => data));

  }

Complete Logs

2020-03-25 15:25:16.803  WARN 11624 --- [           main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testDataLoader' defined in file [C:\WSs\webflux\demo\target\classes\com\noblockingcase\demo\configuration\TestDataLoader.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'extratoRepository': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: public abstract reactor.core.publisher.Mono com.noblockingcase.demo.repository.ExtratoRepository.retrieveAllExtratosPaged(org.springframework.data.domain.Pageable)
2020-03-25 15:25:19.581  INFO 11624 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-03-25 15:25:19.587 ERROR 11624 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testDataLoader' defined in file [C:\WSs\webflux\demo\target\classes\com\noblockingcase\demo\configuration\TestDataLoader.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'extratoRepository': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: public abstract reactor.core.publisher.Mono com.noblockingcase.demo.repository.ExtratoRepository.retrieveAllExtratosPaged(org.springframework.data.domain.Pageable)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.4.RELEASE.jar:5.2.4.RELEASE]
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at com.noblockingcase.demo.DemoApplication.main(DemoApplication.java:12) ~[classes/:na]

1 Ответ

1 голос
/ 02 апреля 2020

РЕДАКТИРОВАТЬ: уточнить взаимодействие.

Когда вы запрашиваете «страницу» размером 10, на сервере вы получите поток из репо, который будет эмитировать 10 элементов, а затем завершить. Поскольку Angular ожидает 1 ответ, Spring соберет все 10 элементов в Контроллере (неблокирующие) в коллекцию и отправит их после завершения Flux. Клиент Observable выдаст 1 значение (массив), а не 10 дискретных элементов. Вы заметите это в своей Angular полезной нагрузке:

this.http
      .get<any[]>

. Из-за этого Flux хорош, поскольку Spring может использовать согласование содержимого для правильных действий.

Если вы действительно хотите go для Mono, который вы, вероятно, хотите .collectList (). Это изменит ваш Flux на Mono of List.

Mono<List<String>> listMono = Flux.just("foo", "bar", "baz").collectList();

Если вы знаете, что ваш Flux действительно будет только 1 значением, вы можете вызвать .single () для него. Просто знайте, что если вы создадите 0 или> 1 элемент, это приведет к ошибке.

Mono<String> foo = Flux.just("foo").single();
...