1

我正在尝试在 Spring Boot 中动态注册 Bean,但是NoSuchBeanDefinitionException如果尝试自动装配其中一个动态 bean,则创建 bean 的顺序总是会导致。

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

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

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

将工厂 bean 的名称添加@DependsOn到组件导致首先创建工厂,而不是动态 bean。

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

为什么 Spring 以错误的顺序创建我的 bean,我能做些什么来解决这个问题?

编辑:

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

4

2 回答 2

1

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

考虑到在 Spring 4.0 之前,这个注解只用于 AspectJ 的执行顺序。Spring 4.0 之后,支持将注入的组件排序到集合中。因此,Spring 将根据它们的 order 值注入相同类型的自动连接 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具有更高的优先级。0BeanX1

GitHub 演示

相关文章:

于 2018-10-30T20:58:54.137 回答
0

我发现我的问题是由我创建 bean 定义的方式引起的。我使用的是 aGenericBeanDefinition而不是 a RootBeanDefinition。使用后者允许我使用setTargetType()而不是setBeanClass()立即解决问题并导致 Spring 找出创建 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);

我将更新存储库中的示例代码以供进一步参考。

于 2018-11-01T00:16:10.627 回答