Spring Multiple PropertyPlaceholderConfigurer files and database

北城余情 提交于 2019-12-13 05:24:34

问题


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:

  1. Load the file db.Properties
  2. Initialize the beans of DataSources
  3. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!