一. 问题
在实际开发中,通常服务器都很多,每台服务器上的应用都需要配置参数,如果有修改或者变动就会很麻烦,每台服务器都需要改一次。所以有必要把一些参数配置到数据库里,这样方便查看和管理。
来看一个实际例子,我们在Spring中加载配置的代码为:
- <!-- 应用的全局配置文件 -->
- <context:property-placeholder location="classpath*:config.properties" />
- <!-- 定义远程访问 url 的 工具 -->
- <bean id="httpUtils" class="common.web.HttpUtils">
- <property name="directHost" value="${httpUtils.directHost}" />
- <property name="host" value="${root}" />
- <!-- 超时设置 10秒 -->
- <property name="timeOut" value="10000" />
- <!-- 使用代理 -->
- <property name="useProxy" value="${useProxy}" />
- <property name="proxyServer" value="${proxyServer}" />
- <property name="proxyPort" value="${proxyPort}" />
- <property name="proxyUser" value="${proxyUser}" />
- <property name="proxyPassword" value="${proxyPassword}" />
- <property name="proxyServerType" value="${proxyType}" />
- <property name="defaultHeaders">
- <map>
- <entry key="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; Trident/4.0; GTB6; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; Media Center PC 5.0)" />
- <entry key="Cookie" value="BX=4ec9sn94jpfsc&b=3&s=u6; cna=/6HDAZV3zUUBAf6fYMrpHz8u; t=858409c6a38498fadf40289e2e828abd; " />
- </map>
- </property>
- </bean>
我们把配置都集中放到config.properties里,方便管理。如果是变动了,所有服务器上都需要更改一次。
二. Spring 的已有解决办法
http://www.springbyexample.org/twiki/bin/view/Example/SpringModulesWithCommonsConfiguration
这链接介绍了如何实现,主要通过Spring Module 和 Common Configuration 来实现。
- <!-- Required to donnect to datasource -->
- <bean name="PropertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="properties" ref="CommonsConfigurationFactoryBean"/>
- </bean>
- <bean name="CommonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
- <constructor-arg ref="DatabaseConfiguration"/>
- </bean>
- <bean name="DatabaseConfiguration" class="org.apache.commons.configuration.DatabaseConfiguration">
- <constructor-arg type="javax.sql.DataSource" ref="someDataSource"/>
- <constructor-arg index="1" value="TEST_SCHEMA.APPLICATION_PROPERTIES_TABLE"/> <constructor-arg index="2" value="KEY"/> <constructor-arg index="3" value="VALUE"/>
- </bean>
- <!-- Included to elaborate functionality -->
- <bean name="PropertiesPrinter" class="example.PropertiesPrinter" init-method="displayAllProperties">
- <property name="fileLocation" value="${file.location}"/> <property name="petDogsName" value="${pet.dogs.name}"/>
- <property name="keyOne" value="${key.one}"/>
- </bean>
三. 我们的实现
以上方法是通过commons-configuration 来实现,这种每次读取和变动都会访问数据库,在我们的实际应用中,应该是启动的时候读取一次就可以了,没有必要占着数据库连接,而且全局配置不允许应用修改的。
借鉴以上方法,我们写了一个类似的类来实现。
配置如下:
- <!-- 采用数据库读取配置 -->
- <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="properties" ref="dataBaseProperties"/>
- <!-- 文件会覆盖数据库配置 -->
- <property name="locations">
- <list>
- <value>classpath*:config.properties</value>
- </list>
- </property>
- lt;/bean>
- <!-- 第一次读入,如果更改,需要重启 数据库配置,这里不能用${} -->
- <bean id="dataBaseProperties" class="common.spring.DatabaseProperties" >
- <constructor-arg type="javax.sql.DataSource" ref="confDataSource"/>
- <constructor-arg value="select key_s,value_s from app_conf where status>0"/>
- </bean>
- <bean id="confDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost/test?characterEncoding=gbk"/>
- <property name="username" value="root"/>
- <property name="password" value=""/>
- </bean>
文件和数据库混合配置,文件的配置优先。
源码如下:
- /**
- * 专注互联网,分享创造价值
- * maoxiang@gmail.com
- */
- package common.spring;
- import common.util.ValidateUtil;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.util.Properties;
- import javax.sql.DataSource;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.beans.factory.FactoryBean;
- import org.springframework.beans.factory.InitializingBean;
- /**
- * 从数据库里读取配置
- * 实现参考http://forum.springsource.org/showthread.php?t=57246
- */
- public class DatabaseProperties implements InitializingBean, FactoryBean {
- private static final Log log = LogFactory.getLog(DatabaseProperties.class);
- private Properties props = new Properties();
- private DataSource datasource; //数据源
- private String query; //读取的sql
- public DatabaseProperties(DataSource datasource, String query) {
- this.datasource = datasource;
- this.query = query;
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- initProperties();
- }
- @Override
- public Object getObject() throws Exception {
- return props;
- }
- @Override
- public Class getObjectType() {
- return Properties.class;
- }
- @Override
- public boolean isSingleton() {
- return true;
- }
- //----------
- private void initProperties() {
- Connection connection = null;
- try {
- connection = datasource.getConnection();
- PreparedStatement ps = connection.prepareStatement(query);
- ResultSet rs = ps.executeQuery();
- while (rs.next()) {
- String key = rs.getString(1);
- String value = rs.getString(2);
- if (!ValidateUtil.isNull(key) && !ValidateUtil.isNull(value)) {
- log.info("load property. Key=" + key + ",Value=" + value);
- props.setProperty(key, value);
- }
- }
- rs.close();
- ps.close();
- } catch (Exception e) {
- log.error(e);
- } finally {
- if (connection != null) {
- try {
- connection.close();
- } catch (Exception e) {
- log.error(e);
- }
- }
- }
- }
- }