it-source

Spring Boot에서 프로그래밍 방식으로 콩을 작성하려면 어떻게 해야 하나요?

criticalcode 2023. 3. 22. 21:38
반응형

Spring Boot에서 프로그래밍 방식으로 콩을 작성하려면 어떻게 해야 하나요?

application.properties에 여러 데이터 소스 설정이 있는 앱을 가지고 있습니다.나는 가지고 있다@ConfigurationProperties이 설정을 로드하는 클래스입니다.이제 여기서 값을 취하겠습니다.ConfigurationProperties이를 사용하여 DataSource 빈을 즉시 만듭니다.사용해보았습니다.@PostConstruct및 구현BeanFactoryPostProcessor단,BeanFactoryPostProcessor,처리가 너무 이른 것 같습니다 - 제 작업 전에ConfigurationProperties클래스가 채워졌습니다.속성을 읽고 작성하려면 어떻게 해야 합니까?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

또한 Configuration Properties 클래스:

@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);
            }
        }
    }

마지막으로@PostConstructmethod를 만듭니다.BasicDataSourceApplicationContext에 추가합니다.다만, 이것을 실장하려고 하면,BeanFactoryPostProcessor및 구현postProcessBeanFactory,그clients속성은 null입니다.CLIENT_DATASOURCES내가 채워넣은 것@PostConstruct.

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    System.out.println("clients: " + CLIENT_DATASOURCES);
}

Spring Boot을 사용하여 데이터 소스를 즉시 생성하는 가장 좋은 방법은 무엇입니까?

당신의 콩을 만들고 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();
    }
}

이러한 콩은 빈 이름 검색을 통해서만 사용할 수 있습니다.괜찮으시다면 말씀해주세요.

당신의 사용 사례를 보여주기 위해 github에 샘플 프로젝트를 만들었습니다.

https://github.com/lhotari/dynamic-datasources

콩을 추가하기 위해 ImportBeanDefinitionRegistrar를 구현했습니다.EnvironmentAware를 구현하면 설정을 입수할 수 있습니다.목표를 달성하기 위한 다른 방법이 있을 수 있지만 GspAutoConfiguration에서 사용한 방법으로 콩을 동적으로 등록했습니다.GspAutoConfiguration을 사용하면 스프링 부트 어플리케이션에서 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;
    }

}

언급URL : https://stackoverflow.com/questions/25160221/how-do-i-create-beans-programmatically-in-spring-boot

반응형