简体   繁体   English

Spring Boot Factory Bean 创建顺序

[英]Spring Boot Factory Bean Creation Order

I am trying to dynamically register Beans in Spring Boot, however the order of how the beans are created always result in a NoSuchBeanDefinitionException if it try to autowire one of the dynamic beans.我正在尝试在 Spring Boot 中动态注册 Bean,但是如果尝试自动装配动态 bean 之一,则创建 bean 的顺序总是会导致NoSuchBeanDefinitionException

My setup consists of two projects, one spring-boot-starter project and the actual spring-boot application.我的设置包括两个项目,一个 spring-boot-starter 项目和实际的 spring-boot 应用程序。

The actual application registers a BeanDefinitionRegistryPostProcessor that adds the bean definitions.实际应用程序注册一个BeanDefinitionRegistryPostProcessor来添加 bean 定义。 The instances itself are constructed via another bean defined in the starter project that itself takes another bean as dependency.实例本身是通过启动项目中定义的另一个 bean 构建的,该 bean 本身将另一个 bean 作为依赖项。

In order to consume the dynamically registered bean, I created a class annotated with @Component and defined a constructor expecting said bean as parameter.为了使用动态注册的 bean,我创建了一个用@Component注释的@Component并定义了一个以该 bean 作为参数的构造函数。 When I debug the application by setting @Autowired(required=false) , I can see that the constructor of my component is called before the dynamic bean has been created.当我通过设置@Autowired(required=false)调试应用程序时,我可以看到在创建动态 bean 之前调用了我的组件的构造函数。 Moreover, not even the factory bean has been created at that time.此外,当时甚至还没有创建工厂 bean。

Added @DependsOn with the factory bean's name to component resulted the factory being created first, but not the dynamic beans. @DependsOn带有工厂 bean 名称的@DependsOn添加到组件导致首先创建工厂,而不是动态 bean。

Setting @DependsOn with the dynamic bean's name works, but that doesn't seem to be the correct way to solve this issue.使用动态 bean 的名称设置@DependsOn有效,但这似乎不是解决此问题的正确方法。

Why is Spring creating my beans in the wrong order and what can I do solve this?为什么 Spring 以错误的顺序创建我的 bean,我能做些什么来解决这个问题?

EDIT:编辑:

I was able to reproduce the issue in a sample repository:我能够在示例存储库中重现该问题:
https://github.com/maveeee/spring-dynamic-bean-demo/ https://github.com/maveeee/spring-dynamic-bean-demo/

You can use the @Order annotation which defines the sort order for an annotated component or bean.您可以使用@Order注释来定义带注释的组件或 bean 的排序顺序。

Take into account that before Spring 4.0, this annotation was used only for the AspectJ execution order.考虑到在 Spring 4.0 之前,此注解仅用于 AspectJ 执行顺序。 After Spring 4.0, it is supported the ordering of injected components to a collection.在 Spring 4.0 之后,支持将注入的组件排序到集合中。 Thus, Spring will inject the auto-wired beans of the same type based on their order value.因此,Spring 将根据它们的顺序值注入相同类型的自动连接 bean。

For example:例如:

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();
    }
}

Will print:将打印:

---@Bean---

BeanY
BeanX

Because BeanY has higher precedence ( 0 , lower value) over BeanX (higher value, 1 ).因为BeanYBeanX (较高的值, 1 )具有更高的优先级( 0 ,较低的值)。

GitHub Demo GitHub 演示

Related articles:相关文章:

I figured out that my issue resulted from how I created the bean definition.我发现我的问题是由我创建 bean 定义的方式引起的。 I was using a GenericBeanDefinition instead of a RootBeanDefinition .我使用的是GenericBeanDefinition而不是RootBeanDefinition Using the later allowed me to use setTargetType() instead of setBeanClass() which immediately resolved the issue and resulted in Spring figuring out the correct order to create the beans so that I could inject the dynamically created bean via @Autowired .使用后者允许我使用setTargetType()而不是setBeanClass()这立即解决了问题并导致 Spring 找出创建 bean 的正确顺序,以便我可以通过@Autowired注入动态创建的 bean。

Before:前:

        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);

After:后:

        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);

I will update the sample code in the repository for further reference.我将更新存储库中的示例代码以供进一步参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM