![](/img/trans.png)
[英]How do I inject spring beans into non-managed beans in a Spring boot console application?
[英]How do I create beans programmatically in Spring Boot?
我有一个应用程序,其中包含许多列在 application.properties 中的数据源设置。 我有一个加载这些设置的@ConfigurationProperties
类。 现在我想从这个ConfigurationProperties
类中获取值并使用它们来即时创建数据源 bean。 我试过使用@PostConstruct
并实现BeanFactoryPostProcessor
。 但是,对于BeanFactoryPostProcessor
,处理似乎发生得早——在我的ConfigurationProperties
类被填充之前。 如何使用 Spring Boot 即时读取属性和创建DataSource
bean?
这是我的 application.properties 的样子:
ds.clients[0]=client1|jdbc:db2://server/client1
ds.clients[1]=client2,client3|jdbc:db2://server/client2
ds.clients[2]=client4|jdbc:db2://server/client4
ds.clients[3]=client5|jdbc:db2://server/client5
还有我的 ConfigurationProperties 类:
@Component
@ConfigurationProperties(prefix = "ds")
public class DataSourceSettings {
public static Map<String, String> CLIENT_DATASOURCES = new LinkedHashMap<>();
private List<String> clients = new ArrayList<>();
public List<String> getClients() {
return clients;
}
public void setClients(List<String> clients) {
this.clients = clients;
}
@PostConstruct
public void configure() {
for (String client : clients) {
// extract client name
String[] parts = client.split("\\|");
String clientName = parts[0];
String url = parts[1];
// client to datasource mapping
String dsName = url.substring(url.lastIndexOf("/") + 1);
if (clientName.contains(",")) {
// multiple clients with same datasource
String[] clientList = clientName.split(",");
for (String c : clientList) {
CLIENT_DATASOURCES.put(c, dsName);
}
} else {
CLIENT_DATASOURCES.put(clientName, dsName);
}
}
}
在这个@PostConstruct
方法的最后,我想用这些设置创建一个BasicDataSource
并将它添加到 ApplicationContext。 但是,如果我尝试通过实现BeanFactoryPostProcessor
并实现postProcessBeanFactory
来做到这一点,则clients
属性为 null,我使用@PostConstruct
填充的CLIENT_DATASOURCES
如此。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("clients: " + CLIENT_DATASOURCES);
}
使用 Spring Boot 即时创建数据源的最佳方法是什么?
如何创建您的 bean 并要求 Spring Boot 向其中注入值?
就像是
@Bean
@ConfigurationProperties("ds.client1")
public DataSource dataSourceClient1() {
DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("ds.client2")
public DataSource dataSourceClient2() {
DataSourceBuilder.create().build();
}
然后, ds.client1
命名空间中的任何设置都属于第一个数据源(即ds.client1.password
是该DataSource
的数据源密码)。
但也许您不知道您将拥有多少数据源? 这变得越来越复杂,尤其是当您需要将这些动态数据源注入其他对象时。 如果您只需要按名称查找它们,您可以将它们自己注册为单身人士。 这是一个有效的例子
@ConfigurationProperties(prefix = "ds")
public class DataSourceSettings implements BeanFactoryAware {
private List<String> clients = new ArrayList<>();
private BeanFactory beanFactory;
public List<String> getClients() {
return clients;
}
public void setClients(List<String> clients) {
this.clients = clients;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@PostConstruct
public void configure() {
Map<String, String> clientDataSources = new HashMap<String, String>();
for (String client : clients) {
// extract client name
String[] parts = client.split("\\|");
String clientName = parts[0];
String url = parts[1];
// client to datasource mapping
String dsName = url.substring(url.lastIndexOf("/") + 1);
if (clientName.contains(",")) {
// multiple clients with same datasource
String[] clientList = clientName.split(",");
for (String c : clientList) {
clientDataSources.put(c, url);
}
}
else {
clientDataSources.put(clientName, url);
}
}
Assert.state(beanFactory instanceof ConfigurableBeanFactory, "wrong bean factory type");
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
for (Map.Entry<String, String> entry : clientDataSources.entrySet()) {
DataSource dataSource = createDataSource(entry.getValue());
configurableBeanFactory.registerSingleton(entry.getKey(), dataSource);
}
}
private DataSource createDataSource(String url) {
return DataSourceBuilder.create().url(url).build();
}
}
请注意,这些 bean只能通过 bean 名称查找来使用。 如果这对你有用,请告诉我。
我在 github 上创建了一个示例项目来演示您的用例。
https://github.com/lhotari/dynamic-datasources
我实现了一个ImportBeanDefinitionRegistrar来添加 bean。 您可以通过实现EnvironmentAware 来掌握配置。 可能还有其他方法可以实现您的目标,但这是我在GspAutoConfiguration 中用于动态注册 bean 的方式。 GspAutoConfiguration 使 Grails GSP 在 Spring Boot 应用程序中可用。
下面是动态数据源示例中的相关配置类: https : //github.com/lhotari/dynamic-datasources/blob/master/src/main/groovy/sample/DynamicDataSourcesConfiguration.java
package sample;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.validation.BindException;
@Configuration
public class DynamicDataSourcesConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
private static Map<String, Object> defaultDsProperties = new HashMap<String, Object>() {
{
put("suppressClose", true);
put("username", "sa");
put("password", "");
put("driverClassName", "org.h2.Driver");
}
};
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment)environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
DataSourceSettings settings = resolveSettings();
for (Entry<String, String> entry : settings.clientDataSources().entrySet()) {
createDsBean(registry, entry.getKey(), entry.getValue());
}
}
private void createDsBean(BeanDefinitionRegistry registry, String beanName, String jdbcUrl) {
GenericBeanDefinition beanDefinition = createBeanDefinition(SingleConnectionDataSource.class);
beanDefinition.getPropertyValues().addPropertyValues(defaultDsProperties).addPropertyValue("url", jdbcUrl);
registry.registerBeanDefinition(beanName, beanDefinition);
}
private GenericBeanDefinition createBeanDefinition(Class<?> beanClass) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_NO);
return beanDefinition;
}
private DataSourceSettings resolveSettings() {
DataSourceSettings settings = new DataSourceSettings();
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(settings);
factory.setTargetName("ds");
factory.setPropertySources(environment.getPropertySources());
factory.setConversionService(environment.getConversionService());
try {
factory.bindPropertiesToTarget();
}
catch (BindException ex) {
throw new FatalBeanException("Could not bind DataSourceSettings properties", ex);
}
return settings;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.