`
tangay
  • 浏览: 93971 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

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

阅读更多

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

 

解决方案的技术思路:

 

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

 

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

 


 

文件简介:

 

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;
	}

	@Override
	public 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 change
		monitor = 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;
	}

	@Override
	public 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);
	}
	
	@Override
	public 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:2181
zookeeperPort = 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" class="ConfigManager"/>
	
	<bean id="concreteConfig" class="ConcreteConfig">
  		<replaced-method name="getZookeeperQuorum" replacer="configManager"/>
  		<replaced-method name="getZookeeperPort" replacer="configManager"/>
  	</bean>
	
</beans>

 

 

测试过程:

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

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

 

 

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

相关推荐

    Spring Boot的自动化配置实现swagger2引入spring boot生成API文档.docx

    swagger-spring-boot-starter该项目主要利用Spring Boot的自动化配置特性来实现快速的将swagger2引入spring boot应用来生成API文档,简化原生使用swagger2的整合代码。 swagger-bootstrap-ui是springfox- swagger的...

    WebServiceConfig java springboot利用Apache CXF创建webserice接口配置类

    在构造过程中,SpringBusFactory会搜索META-INF/cxf(包含在 CXF 的jar中)下的所有bean配置文件。 * 根据这些配置文件构建一个ApplicationContext。开发者也可以提供自己的配置文件来定制Bus。

    SpringBatch+Spring+Mybatis+MySql (spring batch 使用jar)

    1、利用Spring编程模型:使程序员专注于业务处理,让Spring框架管理流程。 2、明确分离批处理的执行环境和应用。 3、提供核心的,共通的接口。 4、提供开箱即用(out of the box)的简单的默认的核心执行接口。 5、...

    spring.net中文手册在线版

    14.5.1.理解Spring.NET声明式事务管理的实现 14.5.2.第一个例子 14.5.3.Transaction特性的设置 14.5.4.通过AutoProxyCreator使用声明式事务 14.5.5.通过TransactionProxyFactoryObject使用声明式事务 14.5.6. 通过...

    Spring面试题

    ☆ Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 ☆ Spring AOP:通过配置管理特性,Spring AOP ...

    Spring in Action(第2版)中文版

    16.1.3委托spring配置的动作 16.1.4关于struts2 16.2协同使用spring和webwork2/struts2 16.3集成spring和tapestry 16.3.1集成spring和tapestry3 16.3.2集成spring和tapestry4 16.4协同使用spring和jsf 16.4.1...

    Spring in Action(第二版 中文高清版).part2

    16.1.3 委托Spring配置的动作 16.1.4 关于Struts 2 16.2 协同使用Spring和WebWork 2/Struts 2 16.3 集成Spring和Tapestry 16.3.1 集成Spring和Tapestry 3 16.3.2 集成Spring和Tapestry 4 16.4 协同使用...

    Spring in Action(第二版 中文高清版).part1

    16.1.3 委托Spring配置的动作 16.1.4 关于Struts 2 16.2 协同使用Spring和WebWork 2/Struts 2 16.3 集成Spring和Tapestry 16.3.1 集成Spring和Tapestry 3 16.3.2 集成Spring和Tapestry 4 16.4 协同使用...

    +Flex+集成到+Java+EE+应用程序的最佳实践(完整源代码)

    BlazeDS 将读取 services-config.xml 配置文件,该配置文件又引用了 remoting-config.xml、proxy-config.xml 和 messaging-config.xml 这 3 个配置文件,所以,一共需要 4 个配置文件。 由于 BlazeDS 需要将 Java ...

    将 Flex 集成到 Java EE 应用程序的最佳实践(完整源代码)

    BlazeDS 将读取 services-config.xml 配置文件,该配置文件又引用了 remoting-config.xml、proxy-config.xml 和 messaging-config.xml 这 3 个配置文件,所以,一共需要 4 个配置文件。 由于 BlazeDS 需要将 Java ...

    java开源包3

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

    java开源包4

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

    webx3框架指南PDF教程附学习Demo

    • 扩展性 —— Webx 3.0对Spring做了扩展,使Spring Bean不再是“bean”,而是升级成“组件”。一个组件可以扩展另一个组件,也可以被其它组件扩展。这种机制造就了Webx的非常好的扩展性,且比未经扩展的Spring更易...

    好用的代码生成源码

    该特性对如生成的spring配置内容插入spring配置文件十分有用 创建一个生成器模板文件 在temppate目录创建一个文件,如${className}SpringControler.java 则代码生成器会自动将该模板文件加载并生成该文件 充分利用...

    Sosoo 1.0网络爬虫程序.doc

    设置要访问的url列表【allowedURLs】:这是一个Vector类型的变量,用户可以把这些url放在配置文件中,用户可以在运行时提供这个变量。 robby.setAllowedURLs(allowed); 定制url检查规则【URLCheck】:用户除了...

    JAVA上百实例源码以及开源项目

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    java开源包1

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

    java开源包11

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

    java开源包2

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

    java开源包6

    [ini4j] 是一个简单的Java类库,用来读写Windows的ini配置文件。同时还包含一个 Java Perferences API 的实现。 拒绝服务测试工具 Port Groper PortGroper 是一款java写的开源拒绝服务测试工具,它不是僵尸网络类的...

Global site tag (gtag.js) - Google Analytics