从属性文件启动动态 Bean 创建



如何根据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
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。我不确定这是否有区别。它只需要加载配置。

您可以

按如下所述注入所有属性(不确定如何使用当前属性结构进行操作,Spring 允许有关属性注入的真正高级功能,此处提供其他示例(

@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 注册的其他示例

有几个选项:

  • 使用编程("功能"(Bean 注册。通过这种方式,注册 bean 是一个函数,您可以使用 for 循环和 if/else 等。Aliaksei的例子证明了这一点。我通常使用在SpringApplicationBuilder()(而不是SpringApplication.run(..)(注册的ApplicationContextInitializer
  • 您可以使用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 {
    }
}

使用环境对其进行测试,看起来它工作正常。不过,您必须在配置中外部化您的注册星才能注入环境。这里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 {
        }
    }
}

应用程序属性

developers=John,Jack,William

最新更新