데이터베이스에서 스프링 부팅 앱 속성 로드
이 문제에 대한 조언이 필요합니다.스프링 부트 어플리케이션에서는 데이터베이스로부터 속성을 로드합니다.예를 들어 (cron 기간, 이메일 데이터), 로드된 데이터로 대응하는 콩을 스프링 빌드하기 위해 애플리케이션 컨텍스트에서 이러한 속성을 내보내야 합니다.내가 어떻게 이럴 수 있지?
애플리케이션을 기동하기 전에 데이터베이스로부터 속성을 로드해, 프로젝트내의 어느 장소에서나 @Value에 액세스 할 수 있도록 하는 경우는, 이 프로세서를 추가하는 것만으로 끝납니다.
public class ReadDbPropertiesPostProcessor implements EnvironmentPostProcessor {
/**
* Name of the custom property source added by this post processor class
*/
private static final String PROPERTY_SOURCE_NAME = "databaseProperties";
private String[] KEYS = {
"excel.threads",
"cronDelay",
"cronDelayEmail",
"spring.mail.username",
"spring.mail.password",
"spring.mail.host",
"spring.mail.port",
"spring.mail.properties.mail.transport.protocol",
"spring.mail.properties.mail.smtp.auth",
"spring.mail.properties.mail.smtp.starttls.enabled",
"spring.mail.properties.mail.debug",
"spring.mail.properties.mail.smtp.starttls.required",
"spring.mail.properties.mail.socketFactory.port",
"spring.mail.properties.mail.socketFactory.class",
"spring.mail.properties.mail.socketFactory.fallback",
"white.executor.threads",
"white.search.threads",
"lot.sync.threads",
"lot.async.threads",
"lot.soap.threads",
"excel.async.threads",
"kpi.threads",
"upload.threads"
};
/**
* Adds Spring Environment custom logic. This custom logic fetch properties from database and setting highest precedence
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> propertySource = new HashMap<>();
try {
// Build manually datasource to ServiceConfig
DataSource ds = DataSourceBuilder
.create()
.username(environment.getProperty("spring.datasource.username"))
.password(environment.getProperty("spring.mail.password"))
.url(environment.getProperty("spring.datasource.url"))
.driverClassName("com.mysql.jdbc.Driver")
.build();
// Fetch all properties
Connection connection = ds.getConnection();
JTrace.genLog(LogSeverity.informational, "cargando configuracion de la base de datos");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT value FROM config WHERE id = ?");
for (int i = 0; i < KEYS.length; i++) {
String key = KEYS[i];
preparedStatement.setString(1, key);
ResultSet rs = preparedStatement.executeQuery();
// Populate all properties into the property source
while (rs.next()) {
propertySource.put(key, rs.getString("value"));
}
rs.close();
preparedStatement.clearParameters();
}
preparedStatement.close();
connection.close();
// Create a custom property source with the highest precedence and add it to Spring Environment
environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
} // class ReadDbPropertiesPostProcessor end
application.properties에서 데이터베이스에 접속하려면 데이터 소스 데이터가 존재해야 합니다.
그런 다음 META-INF 폴더에 spring이라는 이름의 파일을 만듭니다.factories 에는 다음 행을 입력합니다.
org.springframework.boot.env.EnvironmentPostProcessor=test.config.ReadDbPropertiesPostProcessor
그게 다야, 회수된 자산은 어디서든 접근할 수 있을 거야
읽고 싶은 속성을 모두 나열할 필요가 없도록 Bean Post Processor와 Binder를 사용하는 것이 좋다고 생각합니다.다음 코드는 ConfigurationPropertiesBindingPostProcessor를 나타냅니다.
public class PropertiesInsideDatabaseInitializer implements BeanPostProcessor, InitializingBean, ApplicationContextAware {
private JdbcTemplate jdbcTemplate;
private ApplicationContext applicationContext;
private BeanDefinitionRegistry registry;
private Map<String, Object> systemConfigMap = new HashMap<>();
private final String propertySourceName = "propertiesInsideDatabase";
public PropertiesInsideDatabaseInitializer(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
private void bind(ConfigurationPropertiesBean propertiesBean) {
if (propertiesBean == null || hasBoundValueObject(propertiesBean.getName())) {
return;
}
Assert.state(propertiesBean.getBindMethod() == ConfigurationPropertiesBean.BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"
+ propertiesBean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
try {
Bindable<?> target = propertiesBean.asBindTarget();
ConfigurationProperties annotation = propertiesBean.getAnnotation();
BindHandler bindHandler = new IgnoreTopLevelConverterNotFoundBindHandler();
MutablePropertySources mutablePropertySources = new MutablePropertySources();
mutablePropertySources.addLast(new MapPropertySource(propertySourceName, systemConfigMap));
Binder binder = new Binder(ConfigurationPropertySources.from(mutablePropertySources), new PropertySourcesPlaceholdersResolver(mutablePropertySources),
ApplicationConversionService.getSharedInstance(), getPropertyEditorInitializer(), null);
binder.bind(annotation.prefix(), target, bindHandler);
}
catch (Exception ex) {
throw new BeanCreationException("", ex);
}
}
private Consumer<PropertyEditorRegistry> getPropertyEditorInitializer() {
if (this.applicationContext instanceof ConfigurableApplicationContext) {
return ((ConfigurableApplicationContext) this.applicationContext).getBeanFactory()::copyRegisteredEditorsTo;
}
return null;
}
private boolean hasBoundValueObject(String beanName) {
return this.registry.containsBeanDefinition(beanName) && this.registry
.getBeanDefinition(beanName).getClass().getName().contains("ConfigurationPropertiesValueObjectBeanDefinition");
}
@Override
public void afterPropertiesSet() {
String sql = "SELECT key, value from system_config";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
for (Map<String, Object> map : maps) {
String key = String.valueOf(map.get("key"));
Object value = map.get("value");
systemConfigMap.put(key, value);
}
this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
환경의 PropertySources를 수정할 수도 있습니다.Bean Post Processor 인터페이스는 Bean을 작성하기 전에 초기화하기 위해 구현됩니다.
public class PropertiesInsideDatabaseInitializer implements BeanPostProcessor, InitializingBean, EnvironmentAware {
private JdbcTemplate jdbcTemplate;
private ConfigurableEnvironment environment;
private final String propertySourceName = "propertiesInsideDatabase";
public PropertiesInsideDatabaseInitializer(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void afterPropertiesSet() {
if(environment != null){
Map<String, Object> systemConfigMap = new HashMap<>();
String sql = "SELECT key, value from system_config";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
for (Map<String, Object> map : maps) {
String key = String.valueOf(map.get("key"));
Object value = map.get("value");
systemConfigMap.put(key, value);
}
environment.getPropertySources().addFirst(new MapPropertySource(propertySourceName, systemConfigMap));
}
}
@Override
public void setEnvironment(Environment environment) {
if(environment instanceof ConfigurableEnvironment){
this.environment = (ConfigurableEnvironment) environment;
}
}
}
필요에 따라 데이터베이스 값을 사용하여 콩을 수동으로 설정할 수 있습니다(이렇게 하면 Spring CDI 및 부트 데이터베이스 구성을 활용할 수 있습니다).
세션 타임아웃 설정을 예로 들어 보겠습니다.
@SpringBootApplication
public class MySpringBootApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
@Bean
public HttpSessionListener httpSessionListener(){
return new MyHttpSessionListener();
}
}
다음으로 bean을 설정하기 위한 bean 정의입니다.
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyHttpSessionListener implements HttpSessionListener {
@Autowired
private MyRepository myRepository;
@Override
public void sessionCreated(HttpSessionEvent se) {
se.getSession().setMaxInactiveInterval(this.myRepository.getSessionTimeoutSeconds());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// Noop
}
}
주의: 데이터베이스 콜을 로 이동할 수 있습니다.@PostConstruct
각 세션에 대해 작성하지 않도록 하는 방법.
org.apache.commons:commons-configuration 2:jar:2.8.0 의존관계에서 DatabaseConfiguration 클래스를 사용하면 생활이 훨씬 쉬워집니다.스프링 부트 2.7.3(스프링 5.3.22)과 연계하여 다음 설정이 가능했습니다.
전제 조건: DataSource 유형의 빈이 다른 곳에 구성되어 있어야 합니다.
import javax.sql.DataSource;
import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.configuration2.DatabaseConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
@Configuration
public class ConfigFromDb implements ApplicationContextAware
{
@Override
public void setApplicationContext( ApplicationContext aApplicationContext )
{
var dbConfig = aApplicationContext.getBean(DatabaseConfiguration.class);
var props = ConfigurationConverter.getProperties(dbConfig);
var propSource = new PropertiesPropertySource("CONFIGURATION_FROM_DB", props);
var env = (ConfigurableEnvironment) aApplicationContext.getEnvironment();
env.getPropertySources().addLast(propSource);
}
@EventListener
public void onAppStart(ContextRefreshedEvent aEvent)
{
var context = aEvent.getApplicationContext();
var env = context.getEnvironment();
System.out.println( "ConfigKeyFromDB: " + env.getProperty("ConfigKeyFromDB") );
}
@Bean
public static DatabaseConfiguration databaseConfiguration( DataSource aDataSource )
{
var result = new DatabaseConfiguration();
result.setDataSource( aDataSource);
result.setTable( "CONFIGURATION" );
result.setKeyColumn( "CONFIG_KEY" );
result.setValueColumn( "CONFIG_VALUE" );
return result;
}
}
bean DataBaseConfiguration은 DB에서 설정값을 로드할 수 있습니다(이 DB의 param aDataSource에서 액세스가 설정됩니다).
DB에는 "CONFIGION"이라는 이름의 테이블이 있습니다.이 테이블에는 "CONFIG_KEY"라는 컬럼과 "CONFIG_VALUE"라는 컬럼이 있습니다(물론 테이블과 컬럼에는 원하는 이름을 사용할 수 있습니다).
set Application Context 메서드는 컨텍스트에서 bean 위의 값을 가져와 Java Properties 클래스로 추출하고 이를 Properties Property Source로 변환하여 환경에 추가할 수 있습니다.
이벤트 리스너 onAppStart는 이러한 DB 구성 값을 환경에서 가져올 수 있음을 증명하기 위한 것입니다.
스프링 환경에 속성을 추가하는 것만으로는 속성 자리 표시자를 해결할 수 없습니다(예: @Value 주석이 달린 멤버).이를 위해서는 콩을 1개 더 제공해야 합니다.
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Bean
public static PropertySourcesPlaceholderConfigurer dbConfigForVariableResolving(DatabaseConfiguration aDbConfig)
{
var result = new PropertySourcesPlaceholderConfigurer();
var props = ConfigurationConverter.getProperties(aDbConfig);
result.setProperties(props);
return result;
}
언급URL : https://stackoverflow.com/questions/46407230/load-spring-boot-app-properties-from-database
'programing' 카테고리의 다른 글
유니코드 문자를 표시/복호화하는 Javascript 대응 (0) | 2023.03.31 |
---|---|
인젝터가 이미 생성되었습니다.모듈을 등록할 수 없습니다. (0) | 2023.03.31 |
Facebook을 WordPress 등록/로그인에 통합하는 방법 (0) | 2023.03.31 |
React에 필요한 프로포즈를 1개 이상 포함 (0) | 2023.03.31 |
div 클릭 시 열린 파일 브라우저 반응 (0) | 2023.03.31 |