首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

利用Spring兑现配置文件动态替换

2012-09-05 
利用Spring实现配置文件动态替换在很多项目里面都有后缀名为properties的配置文件,我们一般会把这些文件放

利用Spring实现配置文件动态替换

在很多项目里面都有后缀名为properties的配置文件,我们一般会把这些文件放到名为conf之类的目录下面,随同jar一起发布。运行时会把conf目录加到jvm的classpath下面去。麻烦的是,程序运行时,我们改动了配置文件,如何让我们的配置文件无需重启程序起作用。我这里有个比较简陋的解决方案,有兴趣的可以看看,应该还可以做些优化。

?

解决方案的技术思路:

?

起一个定时器,定时的监控配置的文件的修改时间,如果一旦发现修改,重新装载文件。由于Spring的配置值表达式不支持OGNL类的表达式,于是使用Spring自带的method replace(方法替换)来模拟OGNL类的表达。

?

代码并不复杂,用到包有asm,cglib,spring2.x,commons-logging4个而已。demo结构如下:

?


利用Spring兑现配置文件动态替换

?

文件简介:

?

FileListener:监测配置文件修改的接口

FileMonitor:一个TimeTask的子类,检查文件有无改动

ConfigManager: 核心类,里面有个Properties成员装载配置文件信息

ConcreteConfig:配置的“反射”类

Main:测试类

?

conf/monitor.properties 配置文件

conf/monitorContext.xml Spring配置文件

?

具体实现代码为:

?

FileListener.java

?

import java.io.File;/** * an interface to listen the notifications when the file has been changed *  */public interface FileListener {/** * a notification when the file changed *  * @param file *            the file which has been changed */public void onFileChanged(File file);}

?

FileMonitor.java

import java.io.File;import java.util.TimerTask;/** * a class to monitor if the file has been changed *  */public class FileMonitor extends TimerTask {private FileListener listener;private File file;private long lastModified;/** * constructor *  * @param file *            a file which will be monitor * @param listener *            a listener which will be notified when the file has been *            changed */public FileMonitor(File file, FileListener listener) {if (file == null || listener == null) {throw new NullPointerException();}this.file = file;this.lastModified = this.file.lastModified();this.listener = listener;}@Overridepublic void run() {long lastModified = this.file.lastModified();if (this.lastModified != lastModified) {this.lastModified = lastModified;this.listener.onFileChanged(this.file);}}}
?

ConfigManager.java

?

import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.lang.reflect.Method;import java.net.URISyntaxException;import java.net.URL;import java.util.Properties;import java.util.Set;import java.util.Timer;import org.springframework.beans.factory.support.MethodReplacer;public class ConfigManager implements FileListener, MethodReplacer{private static final long FILE_MONITOR_INTERVAL = 5000;private static final String MONITOR_CONF_FILE_PATH = "monitor.properties";private FileMonitor monitor;private Timer timer = new Timer("Timer", true);private Properties properties = new Properties();private ConfigManager() throws IOException {properties.load(getClass().getResourceAsStream("/monitor.properties"));// monitor the configuration file changemonitor = new FileMonitor(getFileByClassPath(MONITOR_CONF_FILE_PATH), this);timer.schedule(monitor, FILE_MONITOR_INTERVAL, FILE_MONITOR_INTERVAL);}private File getFileByClassPath(String filepath) {URL url = getClass().getResource(filepath);if (url == null) {System.err.println("failed to find the file " + filepath);return null;}try {File file = new File(url.toURI());return file;} catch (URISyntaxException e) {e.printStackTrace();}return null;}@Overridepublic synchronized void onFileChanged(File file) {Properties newProperties = new Properties();try {newProperties.load(new FileInputStream(file));} catch (IOException e) {e.printStackTrace();return;}Set<String> keys = properties.stringPropertyNames();for(String key : keys) {String newValue = (String)newProperties.get(key);String oldValue = properties.getProperty(key);System.out.println("newValue:"+newValue+" oldValue:"+oldValue);if(newValue != null) {properties.setProperty(key, newValue);}else {properties.remove(key);}}}public synchronized? String getProperty(String key) {return properties.getProperty(key);}@Overridepublic Object reimplement(Object arg0, Method arg1, Object[] arg2)throws Throwable {String methodName = arg1.getName();String tmp = methodName.substring("get".length());char ch = tmp.charAt(0);ch = Character.toLowerCase(ch);tmp = ch+tmp.substring(1);return getProperty(tmp);}}

?

ConcreteConfig.java

public class ConcreteConfig {private String zookeeperQuorum;private String zookeeperPort;/** * @param zookeeperQuorum the zookeeperQuorum to set */public void setZookeeperQuorum(String zookeeperQuorum) {this.zookeeperQuorum = zookeeperQuorum;}/** * @return the zookeeperQuorum */public String getZookeeperQuorum() {return zookeeperQuorum;}/** * @param zookeeperPort the zookeeperPort to set */public void setZookeeperPort(String zookeeperPort) {this.zookeeperPort = zookeeperPort;}/** * @return the zookeeperPort */public String getZookeeperPort() {return zookeeperPort;}}

?

Main.java

import org.springframework.context.support.FileSystemXmlApplicationContext;public class Main {public static void main(String[] args) throws InterruptedException {FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(                new String[]{                        "classpath:monitorContext.xml"                });ConcreteConfig config = (ConcreteConfig)context.getBean("concreteConfig");while(true) {System.out.println(config.getZookeeperQuorum());System.out.println(config.getZookeeperPort());Thread.sleep(5000);}}}
?

monitor.properties

?

zookeeperQuorum = host1:2181,host2:2181,host3:2181zookeeperPort = 2181
?

?

monitorContext.xml

?

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans default-lazy-init="false"><bean id="configManager" replacer="configManager"/>  <replaced-method name="getZookeeperPort" replacer="configManager"/>  </bean></beans>

?

?

测试过程:

在Eclipse里面以Main做主类运行,观察控制台输出。

然后改动monitor.properties,再看控制台输出,可以发现改动很快生效。

?

?

1 楼 myyate 2010-12-29   war包部署直接打开war包替换properties文件? 2 楼 tangay 2010-12-29   不是这样啦,现在一般软件home目录下面都有个conf,配置文件一般都放在这下面,启动时候把conf目录加入classpath里面的。war包部署的我倒是没考虑过,如果是war的话,我建议用jmx来管理吧,把配置项做成个MBean,修改应用配置通过读写MBean来完成。 3 楼 tangay 2010-12-31   如果是在分布式环境中,当改动配置时需要影响到所有的节点的情况,建议把配置信息放到zookeeper中,并在需要侦听配置改动的地方配置一个Zookeeper的watcher,这样配置改动就能被watcher侦听到,采取相应的动作。

热点排行