2

我正在使用 Eclipse Juno、JDK 7、tomcat 7.0.29、hibernate 4.1.5 和 Spring 3.1.2。我正在尝试从数据库加载 Spring 配置信息。这是我到目前为止可以做的:

WEB.XML

<context-param>
    <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/context-beans.xml
        </param-value>
</context-param>

jdbc.properties

jdbc.driver_class=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.url=jdbc:sqlserver://localhost;databaseName=mydbname;
jdbc.username=<my_usr>
jdbc.password=<my_pwd>

上下文-beans.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:oxm="http://www.springframework.org/schema/oxm" 
        xmlns:security="http://www.springframework.org/schema/security"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
                        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd">

    <!-- Configure which package will contain our components -->
    <context:component-scan base-package="com.xxx" />

    <context:annotation-config />

    <mvc:annotation-driven />

    <mvc:resources mapping="/static/**" location="/static/" />

    <tx:annotation-driven />

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="appPersistenceUnit" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" p:driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
        p:url="jdbc:sqlserver://localhost;databaseName=appDB" p:username="theUserName" p:password="thePassword" destroy-method="close"/>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" lazy-init="false" >
        <property name="dataSource" ref="dataSource" />
        <property name="lazyInit" value="false" />
    </bean>

    <bean id="appProperties" class="com.xxx.web.util.MyAppPropertyPlaceholderConfigurer">
        <property name="nameColumn" value="key" />
        <property name="valueColumn" value="value" />
        <property name="tableName" value="app_params" />
        <property name="whereClause" value="param_type = 'GLOBAL'" />
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

    <!-- Files path configuration -->
    <mvc:resources mapping="/iconfiles/**" location="file:/${PATH.ICON_FILES}" />

</beans>

请考虑表 app_params 包含 1 条记录,其中 key="PATH.ICON_FILES"、value="c:/temp/icons/" 和 param_type="GLOBAL"。

MyAppPropertyPlaceholderConfigurer

package com.xxx.web.util;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;

public class MyAppPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
    private JdbcTemplate jdbcTemplate;

    private String nameColumn;

    private String valueColumn;

    private String tableName;

    private String whereClause;

    public MyAppPropertyPlaceholderConfigurer() {
        super();
        setPlaceholderPrefix("${");
    }

    @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] %s", nameColumn, valueColumn, tableName, whereClause);
        logger.info("Reading configuration properties from database. Query=" + sql);

        try {
            jdbcTemplate.query(sql, new RowCallbackHandler() {
                @Override
                public void processRow(ResultSet rs) throws SQLException {
                    String name = rs.getString(nameColumn);
                    String value = rs.getString(valueColumn);

                    if (null == name || null == value) {
                        throw new SQLException("Configuration database contains empty data. Name='" + (name == null ? "" : name)
                                + "', Value='" + (value == null ? "" : value) + "'.");
                    }

                    props.setProperty(name, value);
                }
            });
        } 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 '" + tableName
                    + "'.");
        } else {
            logger.info("Application config info loaded from configuration database.");
        }
    }

    @Override
    protected String convertPropertyValue(String originalValue) {

        if (originalValue.startsWith("!!")) {
            // TODO ex: return EncryptUtil.decrypt(originalValue); see org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils
            return originalValue;
        }

        return originalValue;
    }

    public void setJdbcTemplate(JdbcTemplate newJdbcTemplate) {
        this.jdbcTemplate = newJdbcTemplate;
    }

    public void setNameColumn(String newNameColumn) {
        this.nameColumn = newNameColumn;
    }

    public void setValueColumn(String newValueColumn) {
        this.valueColumn = newValueColumn;
    }

    public void setTableName(String newTableName) {
        this.tableName = newTableName;
    }

    public void setWhereClause(String newWhereClause) {
        if (newWhereClause == null || newWhereClause.trim().length() == 0) {
            this.whereClause = "";
        } else {
            if (newWhereClause.trim().toUpperCase().startsWith("WHERE ")) {
                this.whereClause = newWhereClause;
            } else {
                this.whereClause = "WHERE " + newWhereClause;
            }
        }
    }

}

这样做可以按预期工作,并且我已正确解析变量 ${PATH.ICON_FILES} 。

但是,我不想让我的 dataSource bean 带有用户名、密码和所有硬编码在 XML 文件中的内容。我想从另一个属性文件加载它,优先加密。

我该怎么做?

我已经尝试通过将以下代码添加到 context-beans.xml 中来添加另一个带有 jdbc 配置的 PropertyPlaceholderConfigurer(此时为纯文本):

<bean id="jdbcConfigurer" name="jdbcConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:jdbc.properties</value>
        </list>
    </property>
</bean>

然后我将 dataSource bean 更改为从那里读取:

<bean id="dataSource" name="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" p:driverClassName="${jdbc.driver_class}"
     p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" destroy-method="close" />

当我尝试运行时,出现以下错误:

[2012-10-10 11:33:48,744] WARN  (SQLErrorCodesFactory.java:227) - Error while extracting database product name - falling back to empty error codes
    org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driver_class}'

知道我在这里做错了什么吗?看起来它是在从常规 PropertyPlaceholderConfigurer 获取值之前实例化 MyAppPropertyPlaceholderConfigurer ...

谢谢。

4

0 回答 0