问题
I want to load the properties from my java application used the Spring Beans, through the database, however I need to load the properties of the connection to the database from a file.
So what I want is the following:
- Load the file db.Properties
- Initialize the beans of DataSources
- Load the remaining properties Base
At the end, have access to the loaded properties from both sources.
I tried the following way.
application-context.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- ************************************* -->
<!-- 1: Files Properties Load -->
<!-- ************************************* -->
<bean id="placeholderPropertiesFile" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:./properties/dataBase.properties</value>
<value>file:./properties/webServicesUserPass.properties</value>
</list>
</property>
<property name="placeholderPrefix" value="$file{" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="0" />
</bean>
<!-- ************************************* -->
<!-- 2: Data Sources Load -->
<!-- ************************************* -->
<bean id="dataSourceOracleRead" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="$file{db.driverClassName}" />
<property name="url" value="$file{dbRead.url}" />
<property name="username" value="$file{dbRead.username}" />
<property name="password" value="$file{dbRead.password}" />
<property name="poolPreparedStatements" value="true" />
</bean>
<bean id="dataSourceOracleWrite" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="$file{db.driverClassName}" />
<property name="url" value="$file{dbWrite.url}" />
<property name="username" value="$file{dbWrite.username}" />
<property name="password" value="$file{dbWrite.password}" />
<property name="poolPreparedStatements" value="true" />
</bean>
<!-- ************************************* -->
<!-- 3: Database Properties Load -->
<!-- ************************************* -->
<bean id="placeholderPropertiesDatabase" class="xxx.xxx.DbPropertyPlaceholderConfigurer" >
<property name="dt" ref="dataSourceOracleRead" />
<property name="table" value="CONFIG0" />
<property name="key" value="I_CODIGO" />
<property name="value" value="C_VALOR" />
<property name="placeholderPrefix" value="$db{" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
DbPropertyPlaceholderConfigurer:
public class DbPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private DataSource dt;
private String key;
private String value;
private String table;
@Override
protected void loadProperties(final Properties props) throws IOException {
if (null == props) {
throw new IOException("No properties passed by Spring framework - cannot proceed.");
}
String sql = String.format("SELECT %s, %s FROM %s", key, value, table);
try {
JdbcTemplate t = new JdbcTemplate(dt);
t.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
String auxKey = rs.getString(key);
String auxValue = rs.getString(value);
if (null == auxKey || null == auxValue) {
throw new SQLException("Configuration database contains empty data. Name='" + (auxKey == null ? "" : auxKey)
+ "', Value='" + (auxValue == null ? "" : auxValue) + "'.");
}
props.setProperty(auxKey, auxValue);
}
});
} catch (Exception e) {
logger.fatal("There is an error in either 'application.properties' or the configuration database.");
throw new IOException(e);
}
if (props.size() == 0) {
logger.fatal("The configuration database could not be reached or does not contain any properties in '" + table
+ "'.");
} else {
logger.info("Application config info loaded from configuration database.");
}
}
public void setDt(DataSource dt) {
this.dt = dt;
}
public void setKey(String key) {
this.key = key;
}
public void setValue(String value) {
this.value = value;
}
public void setTable(String table) {
this.table = table;
}
}
Error:
Exception in thread "Main Thread" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.IOException: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '$file{db.driverClassName}'
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:87)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:661)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:84)
at xxx.xxx.xxx.BatchMain.main(BatchMain.java:35)
Caused by: java.io.IOException: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '$file{db.driverClassName}'
at xxx.xxx.xxx.xxx.propertiesLoader.DbPropertyPlaceholderConfigurer.loadProperties(DbPropertyPlaceholderConfigurer.java:60)
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:161)
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:78)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:664)
... 4 more
If I choose to initialize the bean "dataSourceOracleRead" in application-context.xml instead of getting trough the file, access the database works fine.
<bean id="dataSourceOracleRead" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@10.10.10.10:1521:bdtest" />
<property name="username" value="test" />
<property name="password" value="qwerty" />
<property name="poolPreparedStatements" value="true" />
</bean>
I think when I set the second "PropertyPlaceholderConfigurer" I automatically lose the properties already charged through the first (properties file).
If I have two "PropertyPlaceholderConfigurer" in which one load one of the files and second loads the second file I do not lose the charged properties through the 1st "PropertyPlaceholderConfigurer".
Ps: I'm using version 3.1.2 of spring
回答1:
If you are using Spring 3.1, consider using a PropertySourcePlaceholderConfigurer, which, in combination with a ConfigurableEnvironment, let you add PropertySources dynamically.
That way you can define just one placeholderConfigurer, initialized from property files, and later load additional properties from the database.
Also, check this other question which looks very similar to what you want but uses a different approach.
来源:https://stackoverflow.com/questions/25490563/spring-multiple-propertyplaceholderconfigurer-files-and-database