Spring Boot Factory Bean创建顺序

时间:2018-10-30 20:45:53

标签: java spring spring-boot spring-boot-starter

我正在尝试在Spring Boot中动态注册Bean,但是,如果尝试自动装配动态Bean之一,则创建豆的顺序始终会导致NoSuchBeanDefinitionException

我的设置包括两个项目,一个是spring-boot-starter项目,另一个是实际的spring-boot应用程序。

实际的应用程序注册了一个BeanDefinitionRegistryPostProcessor来添加bean定义。实例本身是通过在启动程序项目中定义的另一个bean构造的,该实例本身将另一个bean作为依赖项。

为了使用动态注册的bean,我创建了一个用@Component注释的类,并定义了一个构造函数,期望将所述bean作为参数。 通过设置@Autowired(required=false)调试应用程序时,可以看到在创建动态bean之前调用了组件的构造函数。而且,那时甚至还没有创建工厂bean。

在组件中添加了@DependsOn并带有工厂bean的名称,导致首先创建工厂,而不是动态bean。

使用动态bean的名称设置@DependsOn是可行的,但这似乎并不是解决此问题的正确方法。

为什么Spring会以错误的顺序创建我的bean,我该怎么解决?

编辑:

我能够在示例存储库中重现该问题:
https://github.com/maveeee/spring-dynamic-bean-demo/

2 个答案:

答案 0 :(得分:1)

您可以使用@Order注释来定义带注释的组件或bean的排序顺序。

请注意,在Spring 4.0之前,此注释仅用于AspectJ执行顺序。在Spring 4.0之后,它支持将注入的组件排序到集合中。因此,Spring将根据它们的订单值注入相同类型的自动连线的bean。

例如:

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

相关文章:

答案 1 :(得分:0)

我发现我的问题是由我如何创建bean定义引起的。我使用的是GenericBeanDefinition而不是RootBeanDefinition。使用后者使我可以使用setTargetType()而不是setBeanClass()来解决问题,并导致Spring找出创建bean的正确顺序,以便我可以通过{{1 }}。

之前:

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

我将更新repository中的示例代码,以供进一步参考。