如何根据application.yml文件动态定义bean?
例如,YAML文件如下所示:
service:
host: http://localhost:8080/
account:
url: /account
content-type: application/json
registry:
url: /registry
content-type: application/xml
这会动态创建两个HttpHeaders
标头集Content-Type
。
以下是我现在定义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。我不确定这是否有所作为。它只需要加载配置。
答案 0 :(得分:7)
有两种选择:
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 {
}
}
答案 1 :(得分:2)
您可以按如下所述注入所有属性(不确定如何使用当前属性结构执行此操作,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
}
}
您可以使用
等手动注册beanbeanFactory.registerSingleton("beanName", bean);
此处动态bean注册的其他示例here
答案 2 :(得分:2)
在环境中对其进行测试,看起来工作正常。您必须在配置中外部化registar才能注入环境。
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