Множественные реализации в Spring

У меня есть интерфейс TopicGenerator:

public interface TopicGenerator {
    File create(MultiValueMap params);
    boolean accept(MultiValueMap params);
}

И 3 реализации:

@RequiredArgsConstructor
public class JavaTopicGenerator implements TopicGenerator {
//implementation ommited for readability

@RequiredArgsConstructor
public class PhpTopicGenerator implements TopicGenerator {
//implementation ommited for readability

@RequiredArgsConstructor
public class CppTopicGenerator implements TopicGenerator {
//implementation ommited for readability

Теперь я стараюсь использовать их в зависимости от моих параметров, поэтому я создал специальный TopicFacade.

@RequiredArgsConstructor
public class TopicFacade {

    @NonNull
    private final TopicService topicService;

    @NonNull
    private final List<TopicGenerator> topicGenerators;

    public void generate(MultiValueMap<String, String> params, HttpServletResponse response) {
        for (TopicGenerator topicGenerator : topicGenerators) {
            if (topicGenerator.accept(params)) {
                File tempFile = topicService.generate(params);
                //do something else.
            }
        }
    }
}

Где на моем TopicServiceImpl у меня есть:

@Service
@RequiredArgsConstructor
public class TopicServiceImpl implements TopicService {

    @NonNull
    private final List<TopicGenerator> reportGenerators;

        public File generate(MultiValueMap params) {
        for (TopicGenerator topicGenerator : topicGenerators) {
            if (topicGenerator.create(params)) {
                return topicGenerator.export(params);
            }
        }

Я получаю сообщение об ошибке: Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'topicServiceImpl': Unsatisfied dependency expressed through field 'topicGenerators'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.List<com.topic.service.TopicGenerator>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

(Раньше, когда я использовал инъекцию поля вместо конструктора, я мог добавить @Service до того, как одна из трех реализаций, и код работал над этой единственной реализацией, но это не то, что я ищу)

Где CppTopicGenerator и т. д. Фактически объявлены как бобы? Я просто смотрю классы. Вам нужно аннотировать их стереотипом или использовать класс @Configuration.

Michael 14.11.2018 14:57

@Michael. Я пытался аннотировать их стереотипом (до тех трех классов, которые я пробовал с помощью аннотации службы или аннотации компонентов), возникла ошибка, вызванная: org.springframework.beans.factory.NoSuchBeanDefinitionExcept‌ ion: Нет подходящего bean-компонента типа TopicFacade. : ожидается как минимум 1 bean-компонент, который квалифицируется как кандидат autowire. Аннотации зависимостей: {}

degath 14.11.2018 15:07

@degath вам нужно аннотировать класс каждый стереотипом, если вы хотите использовать внедрение зависимостей. Таким образом, каждый TopicGenerator, Service и Facade нуждается в стереотипной аннотации.

Lino 14.11.2018 15:07

@Lino by every вы имеете в виду эти 3? Правильно? Тогда проверьте мой последний комментарий. Я сделал это и получил ошибку. Я пробовал аннотации служб и компонентов.

degath 14.11.2018 15:10

Ооооо. Дай мне секунду проверить.

degath 14.11.2018 15:10

@degath с каждым, я имею в виду каждый класс, который вы хотите использовать. No qualifying bean of type TopicFacade означает, что вы также должны аннотировать TopicFacade, например, @Service

Lino 14.11.2018 15:11

Я сделал, как вы упомянули, и теперь у меня есть Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationExc‌​eption: Error creating bean with name 'topicServiceImpl': Requested bean is currently in creation: Is there an unresolvable circular reference?

degath 14.11.2018 15:13
Is there an unresolvable circular reference означает, что у вас есть класс A, который использует класс B, а B использует A. Вам придется подумать о структуре своего приложения, если это так.
Lino 14.11.2018 15:16

Я сейчас подумаю о структуре приложения. Кажется, я пока все знаю. Большое спасибо. :)

degath 14.11.2018 15:24

@Lino После исправления круговой ошибки у меня сейчас Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionExce‌​ption: No qualifying bean of type 'com.topic.service.TopicGenerator' available: expected single matching bean but found 3: javaTopicGenerator,phpTopicGenerator ,cppTopicGenerator

degath 14.11.2018 15:35
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
10
851
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Используйте аннотацию квалификатор. https://www.tutorialspoint.com/spring/spring_qualifier_annotation.htm

https://stackoverflow.com/a/40844528/4587961

Тогда ваш фасад может иметь все эти реализации. И у вас есть метод, который вернет конкретную реализацию в зависимости от вашего состояния.

Затем инициализируйте свой список после автоматического подключения всех зависимостей. https://stackoverflow.com/a/8519295/4587961

@Service // Try and play around with annotations.
public class TopicFacade {

    @Autowired
    @Qualifier("Service1")
    private final TopicService topicService1;

    //... The same stuff for other services.

    @Autowired
    @Qualifier("ServiceN")
    private final TopicService topicServiceN;

    //Initialize this in the post construct and put all your services there.
    @NonNull
    private final List<TopicGenerator> topicGenerators = new ArrayList();

    @PostConstruct
    public void initTopicGenerators() throws Exception {
        topicGenerators.add(topicService1);
        //And others.
        topicGenerators.add(topicServiceN);
    }

    public void generate(MultiValueMap<String, String> params, HttpServletResponse response) {
        for (ReportGenerator reportGenerator : reportGenerators) {
            if (reportGenerator.accept(params)) {
                File tempFile = topicService.generate(params);
                //do something else.
            }
        }
    }
}

Вы можете привести пример этого: //Initialize this in the post construct and put all your services there. Потому что я действительно не знаю, как это сделать.

degath 14.11.2018 16:05

Я знаю, как использовать конструкцию post для выполнения таких действий, как запуск какого-либо метода, но не знаю, как использовать генераторы тем для put all my services there и initialize.

degath 14.11.2018 16:18

Спасибо, проверю как можно скорее.

degath 14.11.2018 16:20

Также переместите список с услугами в класс TopicFacade. Вам они не нужны в TopicServiceImpl

Yan Khonski 14.11.2018 16:21

Позвольте нам продолжить обсуждение в чате.

Yan Khonski 14.11.2018 19:16
Ответ принят как подходящий

Можно определить bean-компонент типа List<TopicGenerator>, как показано ниже:

@Configuration
public class AppConfig {

    @Autowired
    private TopicGenerator cppTopicGenerator;

    @Autowired
    private TopicGenerator phpTopicGenerator;

    @Autowired
    private TopicGenerator javaTopicGenerator;

    @Bean
    public List<TopicGenerator> topicGeneratorList()
    {
        return Arrays.asList(cppTopicGenerator, phpTopicGenerator, javaTopicGenerator);
    }
}

При этом ваш исходный код должен работать нормально.

На бобы можно ссылаться в camelCase их соответствующего имени класса. Например, cppTopicGenerator будет ссылаться на bean-компонент класса CppTopicGenerator.java. Хотя для большей ясности рекомендуется использовать @Qualifier.

Да, это тоже решение. Я тоже использовал этот подход.

Yan Khonski 14.11.2018 19:18

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