通过环境变量设置WEB项目数据源(Spring)及其项目配置文件路径,动态更新数据源及项目配置

时间:2022-03-12 21:50:24

项目开发中,打包项目WAR后有时会遇到需要更新项目中相关配置的情况。例如,测试环境的数据源在打包发布到生产环境时则需要更改相关数据源配置,现在大部分做法是在项目根目录下建立properties文件,在其中配置相关数据源参数,然后在Spring中动态创建数据源。如下:

application.properties:

## mysql config
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/test?useUnicode\=true&characterEncoding\=utf-8&useOldAliasMetadataBehavior\=true&noAccessToProcedureBodies\=true
jdbc.user=root
jdbc.password=password

jdbc.minPoolSize=5
jdbc.maxPoolSize=20
jdbc.maxIdleTime=1800
jdbc.acquireIncrement=5
jdbc.maxStatements=50
jdbc.initialPoolSize=10
jdbc.idleConnectionTestPeriod=1800
jdbc.acquireRetryAttempts=30

Spring配置数据源截取:

<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:application.properties"></property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="maxIdleTime" value="${jdbc.maxIdleTime}"></property>
<property name="acquireIncrement" value="${jdbc.acquireIncrement}"></property>
<property name="maxStatements" value="${jdbc.maxStatements}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
<property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"></property>
<property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"></property>
</bean>

上面的做法虽然可以通过更新properties文件配置实现数据源的更新,但是更新后需要重新打成War包才能发布应用。那么如果将相关的数据源配置、项目参数配置、日志参数等配置文件放置在项目以外的目录里的话,会不会更方便呢?下面介绍如何通过环境变量设置动态读取项目以外的配置文件。

由于dataSource交给Spring进行管理,所以需要在Spring容器创建数据源前需要将相关参数初始化好,首先想到的就是利用监听器动态设置PropertyPlaceholderConfigurer的locations配置路径。


所需要的Java编码文件有:

1.配置管理 - 读取(外部)配置路径(com.lihong.common.config.ConfigPath.java)

package com.lihong.common.config;

import java.io.File;

/**
* 配置管理 - 读取(外部)配置路径 com.lihong.common.config.ConfigPath.java
*
* @author $Author: lihong $
* @Date:$Date: 2013-03-29 10:21:30 +0800 (星期五, 29 三月 2013) $
* @Id: $Id: ConfigPath.java 464 2013-03-29 02:21:30Z lihong $
* @LastChangedRevision: $LastChangedRevision: $
*/
public class ConfigPath {
/**
* 获取外部配置文件的目录或默认路径
*/
public static String getConfigPath(String envName) {
// 获取启动参数
String configDir = System.getProperty(envName);
if ( configDir == null ) {
// 获取环境变量
configDir = System.getenv(envName);
}
if ( configDir == null ) {
// 获取CLASS目录
configDir = ConfigPath.class.getClassLoader().getResource("").getPath();
}
if ( configDir.startsWith("file:") ) {
// 替换file:
configDir = configDir.replaceFirst("file:", "");
}
return configDir;
}

/**
* 获取外部配置文件(返回evnName配置的目录路径加上文件名 )
*
* @param evnName 配置的是目录名
* @param fileName 文件名
*/
public static String getExtFile(String envName, String fileName) {
String configDir = getConfigPath(envName);
// 判断是否以文件目录分隔符结尾
if ( configDir.endsWith(File.separator) ) {
configDir = configDir + fileName;
} else {
configDir = configDir + File.separator + fileName;
}
return configDir;
}

}

2.相关启动参数文件配置枚举(com.lihong.common.config.ExtEntity.java)

package com.lihong.common.enumeration;

/**
*
* com.lihong.common.config.ExtEntity.java
*
* @author $Author: lihong $
* @Date:$Date: 2013-03-29 10:21:30 +0800 (星期五, 29 三月 2013) $
* @Id: $Id: ExtEntity.java 464 2013-03-29 02:21:30Z lihong $
* @LastChangedRevision: $LastChangedRevision: $
*/
public enum ExtEntity {
/**
* 启动参数:外部配置文件存放地址
*/
EXT_PARAM_NAME("EXT_BASE_PATH"),

/**
* 数据库相关配置文件名:application.properties
*/
DATABASE_CONF_FN("application.properties"),

/**
* 访问URL配置文件名:resource.properties
*/
RESOURCES_CONF_FN("resource.properties"),

/**
* 日志配置文件名:log.properties
*/
LOG_CONF_FN("log.properties");

private String value;

/**
* @return the value
*/
public String getValue() {
return value;
}

ExtEntity(String val) {
this.value = val;
}

}

3.资源文件properties加载类(com.lihong.common.resource.ManagerProperties.java)

package com.lihong.common.resource;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.lihong.tools.ValidateUtil;

/**
*
* com.lihong.common.resource.ManagerProperties.java
*
* @author $Author: lihong $
* @Date:$Date: 2013-03-29 10:21:30 +0800 (星期五, 29 三月 2013) $
* @Id: $Id: CardManagerProperties.java 464 2013-03-29 02:21:30Z lihong $
* @LastChangedRevision: $LastChangedRevision: $
*/
public class ManagerProperties {

private static Map<String, Properties> propertiesMap = new HashMap<String, Properties>();

public static String baseDir;

public static Properties properties;

/**
* 设置加载哪个配置文件
*
* @param fileLocation 配置文件
*/
public static void loadProperties(String filename) {

if ( propertiesMap.containsKey(filename) ) {
properties = propertiesMap.get(filename);
}
String fileLocation = baseDir + filename;
Properties _prop = null;
try {

InputStream is = null;

File file = new File( fileLocation);
if ( file.exists() ) {
is = new FileInputStream(fileLocation);
} else {
is = ManagerProperties.class.getClassLoader().getResourceAsStream(fileLocation.substring(fileLocation.lastIndexOf("/") + 1));
}

_prop = new Properties();
_prop.load(is);

is.close();
propertiesMap.put(filename, _prop);
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
properties = _prop;
}

/**
* 读取资源文件配置
*
* @param key key值
* @param defaultValue 默认值
* @return 返回资源key值对应内容
*/
public static String getProperty(String key, String defaultValue) {
if ( ValidateUtil.isNotEmpty(key) ) {
return ManagerProperties.properties.getProperty(key, defaultValue);
}
return null;
}

/**
* 读取资源文件配置
*
* @param key key值
* @return 返回资源key值对应内容
*/
public static String getProperty(String key) {
if ( ValidateUtil.isNotEmpty(key) ) {
return ManagerProperties.properties.getProperty(key);
}
return null;
}

/**
* @param baseDir the baseDir to set
*/
public static void setBaseDir(String baseDir) {
ManagerProperties.baseDir = baseDir;
}

/**
* @return the propertiesMap
*/
public static Map<String, Properties> getPropertiesMap() {
return propertiesMap;
}

}

4.项目常量参数初始化类(com.lihong.common.resource.DataResourceInit.java)

package com.lihong.common.resource;

import java.io.File;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;

import com.lihong.common.constants.Constants;
import com.lihong.common.enumeration.ExtEntity;

/**
*
* com.lihong.common.resource.DataResourceInit.java
*
* @author $Author: lihong $
* @Date:$Date: 2013-03-29 11:01:53 +0800 (星期五, 29 三月 2013) $
* @Id: $Id: DataResourceInit.java 465 2013-03-29 03:01:53Z lihong $
* @LastChangedRevision: $LastChangedRevision: $
*/
public class DataResourceInit extends PropertyPlaceholderConfigurer {
/**
* 重写构造,加载数据源配置
*/
public DataResourceInit() {
super();
super.setLocation(new FileSystemResource(new File(ManagerProperties.baseDir + ExtEntity.DATABASE_CONF_FN.getValue())));
}

/**
* 初始化当前系统相关资源URL地址
*/
public static void initResourceURL() {
Constants.IMAGE_UPLOAD_SERVER_URL = ManagerProperties.getProperty("image_upload_server_url");
Constants.IMAGE_LOOK_UP_SERVER_URL = ManagerProperties.getProperty("image_search_addr");
}

/**
* 初始化当前系统日志持久化所需级别
*/
public static void initLogPersistenceLevel() {
Constants.CURRENT_LOG_PERSISTENCE_LEVEL = ManagerProperties.getProperty("log_persistence_level");
}

}

5.完成了上述相关Bean的编写后,下面开始编写SystemIntListener监听器(com.lihong.common.listener.SystemIntListener.java),代码如下:

package com.lihong.common.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.lihong.common.config.ConfigPath;
import com.lihong.common.enumeration.ExtEntity;
import com.lihong.common.resource.CardManagerProperties;

/**
*
*
* com.lihong.common.listener.SystemIntListener.java
*
* @author $Author: lihong $
* @Date:$Date: 2013-03-29 11:06:07 +0800 (星期五, 29 三月 2013) $
* @Id: $Id: SystemIntListener.java 466 2013-03-29 03:06:07Z lihong $
* @LastChangedRevision: $LastChangedRevision: $
*/
public class SystemIntListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent event) {

// 销毁配置文件资源
ManagerProperties.properties = null;
// 销毁配置文件MAP资源
ManagerProperties.getPropertiesMap().clear();

}

@Override
public void contextInitialized(ServletContextEvent event) {

// 获取资源文件根目录
String prefix = ConfigPath.getConfigPath(ExtEntity.EXT_PARAM_NAME.getValue()) + "/";

// 设置资源文件根目录
ManagerProperties.setBaseDir(prefix);

// 初始化数据库相关配置文件
ManagerProperties.loadProperties(ExtEntity.DATABASE_CONF_FN.getValue());

// 文件访问URL配置文件
ManagerProperties.loadProperties(ExtEntity.RESOURCES_CONF_FN.getValue());

// 应用日志配置文件
ManagerProperties.loadProperties(ExtEntity.LOG_CONF_FN.getValue());

}

}


6.Spring数据源做如下调整片段:

<bean id="mappings"
class="com.lihong.common.resource.DataResourceInit">
<!--<property name="locations" value="classpath:application.properties"></property>-->
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="maxIdleTime" value="${jdbc.maxIdleTime}"></property>
<property name="acquireIncrement" value="${jdbc.acquireIncrement}"></property>
<property name="maxStatements" value="${jdbc.maxStatements}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
<property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"></property>
<property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"></property>
</bean>


7.最后在web.xml注册监听器即可,如下:

<listener>
<listener-class>com.lihong.common.listener.SystemIntListener</listener-class>
</listener>