【问题标题】:Create N number of beans with BeanDefinitionRegistryPostProcessor使用 BeanDefinitionRegistryPostProcessor 创建 N 个 bean
【发布时间】:2018-11-24 22:26:36
【问题描述】:

我正在尝试使用 BeanDefinitionRegistryPostProcessor 动态创建 N 个 bean。基于this 问题,我选择使用BeanDefinitionRegistryPostProcessor 作为我的用例。

我在application.yml 中定义了以下内容:

app:
  downstream-services:
    rest:
      jsonPlaceHolder:
        url: https://jsonplaceholder.typicode.com/todos
        api-type: io.mateo.dynamicbeans.JsonPlaceHolderApi

这里连接到 ConfigiruationProperties 类:https://github.com/ciscoo/dynamicbeans/blob/master/src/main/java/io/mateo/dynamicbeans/FeignConfigurationProperties.java

然后我想注入 ConfigiruationProperties 类以及我在此处定义的工厂 bean:https://github.com/ciscoo/dynamicbeans/blob/master/src/main/java/io/mateo/dynamicbeans/FeignClientAutoConfiguration.java

所以现在我有以下内容:

https://github.com/ciscoo/dynamicbeans/blob/master/src/main/java/io/mateo/dynamicbeans/FeignClientFactoryPostProcessor.java

@Component
public class FeignClientFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private final FeignConfigurationProperties properties;
    private final FeignClientFactory feignClientFactory;

    public FeignClientFactoryPostProcessor(FeignConfigurationProperties properties, FeignClientFactory feignClientFactory) {
        this.properties = properties;
        this.feignClientFactory = feignClientFactory;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        properties.getDownstreamServices().getRest().forEach((beanName, props) -> makeClient(beanName, props, registry));
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // no-op
    }

    private void makeClient(String beanName, FeignClientProperties props, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(props.getApiType());
        beanDefinition.setInstanceSupplier(() -> feignClientFactory.create(props));
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}

它应该创建的单个 bean 是在这里注入一个服务类:https://github.com/ciscoo/dynamicbeans/blob/master/src/main/java/io/mateo/dynamicbeans/JsonPlaceHolderService.java

我遇到的问题是:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.mateo.dynamicbeans.FeignClientFactoryPostProcessor]: No default constructor found; nested exception is java.lang.NoSuchMethodException: io.mateo.dynamicbeans.FeignClientFactoryPostProcessor.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1262) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ... 17 common frames omitted
Caused by: java.lang.NoSuchMethodException: io.mateo.dynamicbeans.FeignClientFactoryPostProcessor.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3350) ~[na:na]
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2554) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ... 18 common frames omitted

但是当我从两个属性和定义的构造函数中删除 final 关键字时,我得到一个 NullPointerException

那么我如何动态创建 N 个 bean以便它们及时可用 供我的任何 @Service 类使用?


我知道https://spring.io/projects/spring-cloud-openfeign。我在这里重新创建了我的问题,以说明我在动态创建 SOAP 客户端的不同项目中遇到的相同问题。

更新:进行以下更改:https://github.com/ciscoo/dynamicbeans/commit/4f16de9d03271025cd65d95932a3e854c0619c29,现在我可以完成我的用例了。

【问题讨论】:

    标签: spring spring-boot


    【解决方案1】:

    正如您链接到的问题的答案所暗示的,您不能将依赖项注入到 bean 工厂后处理器中。您需要以编程方式绑定它,而不是注入您的配置属性类。在 Spring Boot 2.x 中,即achieved using the Binder API:

    新的 Binder API 也可以在 @ConfigurationProperties 之外直接在您自己的代码中使用。例如,以下内容将绑定到 ListPersonName 对象:

    List<PersonName> people = Binder.get(environment)
        .bind("my.property", Bindable.listOf(PersonName.class))   
        .orElseThrow(IllegalStateException::new);
    

    配置源可以像这样在 YAML 中表示:

    my:
      property:
      - first-name: Jane
        last-name: Doe
      - first-name: John
        last-name: Doe
    

    【讨论】:

    • 效果很好,谢谢!一个侧面问题,是否建议将BeanDefinitionRegistryPostProcessor 标记为@Component 或在@Configuration 类中定义为@Bean
    • 我会将其定义为 @Configuration 类中的 static @Bean 方法。将方法声明为static 意味着可以在不实例化其类的情况下调用bean 方法。这将防止您的后处理器的创建(这需要很早就发生)导致任何东西也比想要的更早创建。
    猜你喜欢
    • 1970-01-01
    • 2015-12-27
    • 2011-08-10
    • 1970-01-01
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 2014-08-24
    相关资源
    最近更新 更多