【问题标题】:Spring Boot Dynamic Bean Creation From Properties File从属性文件创建 Spring Boot 动态 Bean
【发布时间】:2018-06-18 02:25:51
【问题描述】:

如何根据 application.yml 文件动态定义 bean?

例如,YAML 文件如下所示:

service:
   host: http://localhost:8080/
   account:
     url: /account
     content-type: application/json
   registry:
     url: /registry
     content-type: application/xml

这将动态创建两个带有Content-Type 标头集的HttpHeaders

我现在是这样定义 bean 的:

@Bean
public HttpHeaders accountHeaders(
    @Value("${service.account.content-type}") String contentType
) {
    HttpHeaders headers = new HttpHeaders();
    headers.set(HttpHeaders.CONTENT_TYPE, contentType);
    return headers;
}

@Bean
public HttpHeaders registryHeaders(
    @Value("${service.registry.content-type}") String contentType
) {
    HttpHeaders headers = new HttpHeaders();
    headers.set(HttpHeaders.CONTENT_TYPE, contentType);
    return headers;
}

如果我需要添加更多端点,我需要复制和粘贴这些 bean,我希望避免这样做。

注意:这些动态 bean 不需要任何其他 bean。我不确定这是否会有所不同。它只需要加载配置。

【问题讨论】:

  • 我认为您必须考虑编写可配置的拦截器或过滤器来返回这些标头
  • 我不认为可以在这里应用过滤器,因为这些过滤器适用于使用 restTemplate 的外部请求。我从来没有使用过拦截器。有参考吗?

标签: spring spring-boot


【解决方案1】:

您可以按如下所述注入所有属性(不确定如何使用您当前的属性结构来执行此操作,spring 允许有关属性注入的真正高级功能,其他示例here

@ConfigurationProperties(prefix = "yourPrefix")
public class CustomProperties {

  private final Map<String, String> properties = new HashMap<>();

  @Autowired 
  private ApplicationContext applicationContext;      

  @PostConstruct
  public void init() {
    AutowireCapableBeanFactory beanFactory = this.applicationContext.getAutowireCapableBeanFactory();
    // iterate over properties and register new beans
  }

}

您可以使用类似的东西手动注册 bean

beanFactory.registerSingleton("beanName", bean);

动态bean注册的其他例子在这里here

【讨论】:

    【解决方案2】:

    有几个选项:

    • 使用编程(“功能”)bean 注册。这样,注册一个 bean 就是一个函数,您可以使用 for 循环和 if/else 等。Aliaksei 的示例演示了这一点。我通常使用ApplicationContextInitializer 注册的SpringApplicationBuilder()(而不是SpringApplication.run(..))。
    • 您可以使用ImportBeanDefinitionRegistrar。实现它,然后使用BeanDefinitions 注册bean。使用@Import(MyIbdr.class) 导入该类。
    package com.example.dynabeans;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanDefinitionHolder;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.*;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.stereotype.Component;
    
    import java.util.UUID;
    
    @SpringBootApplication
    public class DynabeansApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DynabeansApplication.class, args);
        }
    
    }
    
    class Foo {
    
        private final String id = UUID.randomUUID().toString();
    
        @Override
        public String toString() {
            return "Foo{" + id + "}";
        }
    }
    
    
    @Component
    class FooListener {
    
        private final Log log = LogFactory.getLog(getClass());
    
        FooListener(Foo[] foos) {
            log.info("there are " + foos.length + " " + Foo.class.getName() + " instances.");
        }
    
    }
    
    @Component
    class LoopyBeanRegistrar implements BeanDefinitionRegistryPostProcessor {
    
        private final Log log = LogFactory.getLog(getClass());
        private final int max = (int) (Math.random() * 100);
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            log.info("registering " + max + " beans.");
            for (int i = 0; i < max; i++) {
                BeanDefinitionBuilder gdb = BeanDefinitionBuilder.genericBeanDefinition(Foo.class, () -> new Foo());
                AbstractBeanDefinition abd = gdb.getBeanDefinition();
                BeanDefinitionHolder holder = new BeanDefinitionHolder(abd, Foo.class.getName() + '#' + i, new String[0]);
                BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
            }
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    
        }
    }
    

    【讨论】:

      【解决方案3】:

      使用环境对其进行测试,看起来它工作正常。不过,您必须在配置中将您的注册器外部化以注入环境。 Binder 在这里不是强制性的。 env.getProperty() 会以同样的方式工作。

      @Configuration
      public class DynamicBeansConfiguration {
      
          @Bean
          public BeanDefinitionRegistrar beanDefinitionRegistrar(Environment environment) {
              return new BeanDefinitionRegistrar(environment);
          }
      
          public class BeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor {
              private Environment environment;
      
              public BeanDefinitionRegistrar(Environment environment) {
                  this.environment = environment;
              }
      
              @Override
              public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      
                  List<Developer> developers = Binder.get(environment)
                          .bind("developers", Bindable.listOf(Developer.class))
                          .orElseThrow(IllegalStateException::new);
      
                  developers.forEach(developer -> {
                      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                      beanDefinition.setBeanClass(Developer.class);
                      beanDefinition.setInstanceSupplier(() -> new Developer(developer.getName()));
                      registry.registerBeanDefinition(developer.getName(), beanDefinition);
                  });
              }
      
              @Override
              public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
              }
          }
      }
      

      application.properties

      developers=John,Jack,William
      

      【讨论】:

        猜你喜欢
        • 2012-08-18
        • 1970-01-01
        • 2012-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-18
        • 1970-01-01
        相关资源
        最近更新 更多