[SPRING] 스프링 부트에서 빈을 프로그래밍 방식으로 만드는 방법은 무엇입니까?
SPRING스프링 부트에서 빈을 프로그래밍 방식으로 만드는 방법은 무엇입니까?
application.properties에 나열된 많은 데이터 소스 설정이있는 응용 프로그램이 있습니다. 이 설정을로드하는 @ConfigurationProperties 클래스가 있습니다. 이제이 ConfigurationProperties 클래스의 값을 가져 와서 DataSource Bean을 직접 작성하는 데 사용하려고합니다. @PostConstruct를 사용하고 BeanFactoryPostProcessor를 구현하려했습니다. 그러나 BeanFactoryPostProcessor를 사용하면 처리가 일찍 진행된 것 같습니다. 즉, ConfigurationProperties 클래스가 채워지기 전입니다. Spring Boot로 속성을 읽고 DataSource 빈을 생성하는 방법은 무엇입니까?
내 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를 구현하여이 작업을 수행하려고하면 클라이언트 속성이 null이고 @PostConstruct로 채워진 CLIENT_DATASOURCES도 마찬가지입니다.
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("clients: " + CLIENT_DATASOURCES);
}
Spring Boot로 즉석에서 데이터 소스를 생성하는 가장 좋은 방법은 무엇입니까?
해결법
-
==============================
1.Bean을 생성하고 Boot에 값을 주입하도록 요청하는 것은 어떻습니까?
Bean을 생성하고 Boot에 값을 주입하도록 요청하는 것은 어떻습니까?
좋아하는 것
@Bean @ConfigurationProperties("ds.client1") public DataSource dataSource() { DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("ds.client2") public DataSource dataSource() { 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(); } }
이 빈은 빈 이름 검색에서만 사용할 수 있습니다. 그게 당신을 위해 작동하는지 알려주세요.
-
==============================
2.당신의 유스 케이스를 보여주기 위해 github에 예제 프로젝트를 만들었습니다.
당신의 유스 케이스를 보여주기 위해 github에 예제 프로젝트를 만들었습니다.
https://github.com/lhotari/dynamic-datasources
나는 콩을 추가하기 위해 ImportBeanDefinitionRegistrar를 구현했다. EnvironmentAware를 구현하여 구성을 유지할 수 있습니다. 목표를 달성하는 다른 방법이있을 수도 있지만, GspAutoConfiguration에서 bean을 동적으로 등록하는 방법이었습니다. GspAutoConfiguration은 Spring 부트 애플리케이션에서 Grails GSP를 사용할 수 있도록합니다.
다음은 동적 데이터 소스 샘플의 관련 구성 클래스입니다. 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; } }
from https://stackoverflow.com/questions/25160221/how-do-i-create-beans-programmatically-in-spring-boot by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring에서 프로그래밍 방식으로 현재 활성 / 기본 환경 프로파일을 얻는 방법은 무엇입니까? (0) | 2018.12.30 |
---|---|
[SPRING] 봄에 AOP 로깅? (0) | 2018.12.30 |
[SPRING] org.hibernate.HibernateException : 현재 스레드에 대한 세션을 찾지 못했습니다. (0) | 2018.12.30 |
[SPRING] <aop : scoped-proxy>의 주석 동등 (0) | 2018.12.30 |
[SPRING] Mockito로 Spring에서 autowired @Value 필드를 어떻게 조롱합니까? (0) | 2018.12.30 |