Spring ожидал один совпадающий компонент, но нашел 2

У меня есть такой класс конфигурации Spring, где мне нужно настроить две разные очереди:

@Configuration
public class DataQueue {

    /**
     * Queue to store the {@link SlowVars} read from the PLC
     * @return the queue
     */
    @Bean
    Set<JsonNode> slowVarsQueue() {
        return new CopyOnWriteArraySet<>();
    }

    /**
     * Queue to store the {@link JsonNode} events
     * @return the queue
     */
    @Bean
    Set<JsonNode> eventsQueue() {
        return new CopyOnWriteArraySet<>();
    }
}

В классе, где мне нужно использовать одну очередь, я написал:

@Log4j2
@RestController
@AllArgsConstructor
public class EventsApiImpl implements EventsApi {

    private Set<JsonNode> eventsQueue;
    private MariaEventService mariaEventService;
    private EventManipulationService eventManipulationService;
    private ObjectMapper mapper;

// lots of methods

}

к сожалению, Springs выдает ошибку, начиная с:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
with name 'EventsApiImpl': Unsatisfied dependency expressed through constructor parameter 0:
No qualifying bean of type 'java.util.Set[com.fasterxml.jackson.databind.JsonNode]' available: 
expected single matching bean but found 2: slowVarsQueue,eventsQueue

У двух bean-компонентов разные имена: одно — slowVarsQueue, а другое — eventsQueue, почему Spring жалуется?

Пожалуйста, добавьте код для slowVarsApiImpl. Это компонент, на который жалуется журнал

pochopsp 25.05.2024 18:48

Спасибо, я исправил, этот журнал пришел из другой части кода с той же проблемой.

Stefano Bossi 26.05.2024 07:51
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как указано в вашей ошибке, Spring не может решить, какой компонент использовать. Оба ваших bean-компонента предоставляют один и тот же тип данных, поэтому вам нужно определить, какой из них должен использоваться Spring.

Самый простой способ — просто добавить @Primary к компоненту, который вы хотите использовать. Вы также можете использовать @Qualifier, чтобы выбрать конкретный компонент, который вы хотите использовать.

Обновлено: Кодовые решения для вашего конкретного случая:

1) Используйте @Primary для одного из бобов.

@Configuration
public class DataQueue {

    @Bean
    @Primary
    Set<JsonNode> slowVarsQueue() {
        return new CopyOnWriteArraySet<>();
    }

    @Bean
    Set<JsonNode> eventsQueue() {
        return new CopyOnWriteArraySet<>();
    }
}

Это решение ограничивает использование вами bean-компонента eventsQueue(), то есть он будет доступен только по имени. Любой код, который требует bean-компонента типа Set<JsonNode> и не указывает имя bean-компонента, будет использовать slowVarsQueue(). Если вы хотите использовать eventsQueue() в определенном месте, вам нужно будет указать на него конкретное имя с помощью @Qualifier или других способов ссылки на компоненты по имени.

2) Используйте @Qualifier в аргументах конструктора.

@Log4j2
@RestController
public class EventsApiImpl implements EventsApi {

    private Set<JsonNode> eventsQueue;
    private MariaEventService mariaEventService;
    private EventManipulationService eventManipulationService;
    private ObjectMapper mapper;

    public EventsApiImpl(@Qualifier("eventsQueue") Set<JsonNode> eventsQueue, MariaEventService mariaEventService, EventManipulationService eventManipulationService, ObjectMapper mapper)
    {
        this.eventsQueue = eventsQueue;
        this.mariaEventService = mariaEventService;
        this.eventManipulationService = eventManipulationService;
        this.mapper = mapper;
    }

// lots of methods

}

Этот вариант по-прежнему сохраняет ценность обоих компонентов, а это означает, что другие части вашего кода по-прежнему будут выдавать исключение, если попытаются получить доступ к компоненту типа Set<JsonNode>. Преимущество этого заключается в том, что вы всегда знаете, к какому компоненту вы обращаетесь. Но если вы получаете доступ к этим bean-компонентам во многих местах вашего кода, вам необходимо определить @Qualifier в каждом из этих мест.

3) Используйте @Primary И @Qualifier

Используйте оба фрагмента кода из 1) и 2) для этого решения. Преимущество этого подхода заключается в том, что всякий раз, когда вы получаете доступ к bean-компоненту Set<JsonNode> в любом месте вашего кода, он всегда будет использовать slowVarsQueue(), не создавая исключений. Но если вы хотите использовать eventsQueue() в любом из этих мест, вы можете просто сделать это, используя @Qualifier("eventsQueue").

Насколько я понял из документации Spring, эти два компонента уже имеют разные имена: одно называется «slowVarsQueue», а другое — «eventsQueue», и это имя я использую, пытаясь их внедрить. Разве этого не достаточно для весны?

Stefano Bossi 25.05.2024 22:24

Компоненты имеют разные имена, определяемые именами методов. Ваша проблема возникает при вызове конструктора EventsApiImpl. Этот конструктор определяется аннотацией @AllArgsConstructor. Конструктор определяет только тип требуемого bean-компонента, но не определяет какой-либо bean-компонент с @Qualifier. Поскольку у вас есть два bean-компонента одного типа, сгенерированный конструктор не может решить, какой из них использовать, и выдает исключение. Использование @Primary для одного из компонентов решило бы эту проблему, поскольку конструктор затем возьмет этот компонент.

Fi0x 25.05.2024 22:37

Другие вопросы по теме