Заказ создания фабрики бобов Spring Boot - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь динамически зарегистрировать Бины в Spring Boot, однако порядок создания бинов всегда приводит к NoSuchBeanDefinitionException, если он пытается автоматически подключить один из динамических бинов.

Моя установка состоит из двух проектов, одного проекта spring-boot-starter и реального приложения spring-boot.

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

Чтобы использовать динамически зарегистрированный компонент, я создал класс с аннотацией @Component и определил конструктор, ожидающий указанный компонент в качестве параметра. Когда я отлаживаю приложение, установив @Autowired(required=false), я вижу, что конструктор моего компонента вызывается до создания динамического компонента. Более того, даже фабричный бин не был создан в то время.

Добавлено @DependsOn с именем фабричного компонента в компонент, в результате чего сначала создается фабрика, но не динамические компоненты.

Установка @DependsOn с именем динамического компонента работает, но, похоже, это не правильный способ решения этой проблемы.

Почему Spring создает мои бины в неправильном порядке и что я могу сделать, чтобы решить эту проблему?

EDIT:

Мне удалось воспроизвести проблему в образце репозитория:
https://github.com/maveeee/spring-dynamic-bean-demo/

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

Я понял, что моя проблема возникла из-за того, как я создал определение бина. Я использовал GenericBeanDefinition вместо RootBeanDefinition. Использование последних позволило мне использовать setTargetType() вместо setBeanClass(), что немедленно решило проблему и привело к тому, что Spring определил правильный порядок создания bean-компонентов, чтобы я мог внедрить динамически созданный bean-компонент через @Autowired.

До:

        var identifier = ...    // Some String identifying the bean
        var clazz = ...         // Some class object coming from a dependency

        var beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(clazz);
        beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        beanDefinition.setAutowireCandidate(true);
        beanDefinition.setFactoryBeanName(CONTRACT_FACTORY_BEAN_NAME);
        beanDefinition.setFactoryMethodName(CONTRACT_FACTORY_METHOD_NAME);

        registry.registerBeanDefinition(identifier, beanDefinition);

После того, как:

        var identifier = ...    // Some String identifying the bean
        var clazz = ...         // Some class object coming from a dependency

        var beanDefinition = new RootBeanDefinition();
        beanDefinition.setTargetType(clazz);
        beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        beanDefinition.setAutowireCandidate(true);
        beanDefinition.setFactoryBeanName(CONTRACT_FACTORY_BEAN_NAME);
        beanDefinition.setFactoryMethodName(CONTRACT_FACTORY_METHOD_NAME);

        registry.registerBeanDefinition(identifier, beanDefinition);

Я обновлю пример кода в хранилище для дальнейшего использования.

0 голосов
/ 30 октября 2018

Вы можете использовать аннотацию @Order, которая определяет порядок сортировки для аннотированного компонента или компонента.

Примите во внимание, что до Spring 4.0 эта аннотация использовалась только для порядка выполнения AspectJ. После Spring 4.0 поддерживается упорядочение вставленных компонентов в коллекцию. Таким образом, Spring будет вводить бины с автоматическим подключением одного и того же типа в зависимости от их значения заказа.

Например:

interface IBean {
    String getName();
}

public class BeanX implements IBean {
    public BeanX() {}

    @Override
    public String getName() {
        return "BeanX";
    }
}

public class BeanY implements IBean {
    public BeanY() {}

    @Override
    public String getName() {
        return "BeanY";
    }
}

@Component
public class RandomComponent {
    @Autowired
    private List<IBean> beans;

    @PostConstruct
    public void getBeanValues() {
        System.out.println("\n---@Bean---\n");
        for (IBean b : beans) {
            System.out.println(b.getName());
        }
    }

    @Bean
    @Order(1)
    public IBean getBeanX() {
        return new BeanX();
    }

    @Bean
    @Order(0)
    public IBean getBeanY() {
        return new BeanY();
    }
}

Напечатает:

---@Bean---

BeanY
BeanX

Поскольку BeanY имеет более высокий приоритет (0, более низкое значение) над BeanX (более высокое значение, 1).

GitHub Demo

Статьи по теме:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...