问题
对于java应用中的一些配置文件每次都需要重新启动服务才能重新加载,非常麻烦,故做了一个动态加载资源的程序。
可选方案
使用监听线程监听文件变化,当文件变化时通知程序重新加载配置文件,用到了事件委托模型和观察者模式类似,如下
公共部分
1.Listener接口
package com.hrtc.monitor;
/**
* 监听器接口
* Jul 30, 2008 3:02:28 PM
*/
public interface IMonitorListener {
public void update(MonitorEvent event);
}
2.Event监听事件顶层类
package com.hrtc.monitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 监听事件
* Jul 30, 2008 3:03:12 PM
*/
public class MonitorEvent {
private Object lock = new Object();
private List monitorListenerList = new ArrayList();
public void addListener(IMonitorListener listener) {
synchronized (lock) {
if (!monitorListenerList.contains(listener)) {
monitorListenerList.add(listener);
}
}
}
public void removeListener(IMonitorListener listener) {
synchronized (lock) {
if (monitorListenerList.contains(listener)) {
monitorListenerList.remove(listener);
}
}
}
public void removeAllListener() {
synchronized (lock) {
monitorListenerList.clear();
}
}
/**
* 触发事件可由子类重载
*/
public void fireEvent() {
synchronized (lock) {
for (Iterator it = monitorListenerList.iterator(); it.hasNext();) {
IMonitorListener listener = (IMonitorListener) it.next();
listener.update(this);
}
}
}
}
3.主线程监听类
package com.hrtc.monitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 监听线程类
*
* Jul 30, 2008 3:03:55 PM
*/
public class MonitorThread extends Thread {
private List monitorEventList = new ArrayList();
private long interval;
private boolean isMonitor = false;
private Object lock = new Object();
private static MonitorThread monitor;
synchronized public static MonitorThread getInstance(long intervalSecond) {
if (monitor == null) {
monitor = new MonitorThread(intervalSecond);
}else{
monitor.setInterval(intervalSecond);
}
return monitor;
}
synchronized public static MonitorThread getInstance() {
if (monitor == null) {
monitor = new MonitorThread(1);
}
return monitor;
}
/**
* 构造方法
*
* @param intervalSecond
* 监听间隔时间
*/
public MonitorThread(long intervalSecond) {
this.interval = intervalSecond * 1000;
}
public void addEvent(MonitorEvent event) {
synchronized (lock) {
if (!monitorEventList.contains(event)) {
monitorEventList.add(event);
}
}
}
public void removeEvent(MonitorEvent event) {
synchronized (lock) {
if (monitorEventList.contains(event)) {
monitorEventList.remove(event);
}
}
}
public void removeAllEvent() {
synchronized (lock) {
monitorEventList.clear();
}
}
/**
* 监听主方法,每隔一段间隔触发事件列表
*/
public void run() {
if (isMonitor) {
return;
}
isMonitor = true;
try {
while (isMonitor) {
synchronized (lock) {
for (Iterator it = monitorEventList.iterator(); it
.hasNext();) {
MonitorEvent event = (MonitorEvent) it.next();
event.fireEvent();
}
}
Thread.sleep(interval);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
isMonitor = false;
}
}
/**
* 结束监听,并不是马上结束只是把标致设为结束
*/
public void end() {
isMonitor = false;
}
/**
* 是否正在监听
*
* @return
*/
public boolean isMonitor() {
return isMonitor;
}
public long getInterval() {
return interval;
}
public void setInterval(long interval) {
this.interval = interval;
}
}
应用案例1:可自动检查文件变化的Properties
1.首先定义一个“文件改变事件监听类”继承自MonitorEvent
package com.hrtc.monitor.file;
import java.io.File;
import java.io.IOException;
import com.hrtc.monitor.MonitorEvent;
/**
* 文件改变监听类
* Jul 30, 2008 3:07:05 PM
*/
public class FileChangeMonitorEvent extends MonitorEvent {
private File f;
private long lastModifiedTime;
private long lastLength;
private boolean isChanged = false;
/**
*
* @param f 需要监听的文件
*/
public FileChangeMonitorEvent(File f) {
if (!f.exists()) {
try {
throw new IllegalArgumentException("Path "
+ f.getCanonicalPath() + " dose't exist.");
} catch (IOException e) {
e.printStackTrace();
}
}
this.f = f;
getFileInfo();
}
private void getFileInfo(){
lastModifiedTime = f.lastModified();
lastLength = f.length();
}
/**
* 如果文件已改变则触发事件
*/
@Override
public void fireEvent() {
try {
f = f.getCanonicalFile();
isChanged = lastModifiedTime != f.lastModified() || lastLength != f.length();
if (isChanged) {
super.fireEvent();
getFileInfo();
isChanged = false;
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获得监听的文件
* @return
*/
public File getF() {
return f;
}
}
2.定义PropertiesEx类,以及内部类ReloadPropertiesListener
package com.hrtc.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import com.hrtc.monitor.IMonitorListener;
import com.hrtc.monitor.MonitorEvent;
import com.hrtc.monitor.MonitorThread;
import com.hrtc.monitor.file.FileChangeMonitorEvent;
/**
* 可自动加载属性变化的属性类
* Jul 30, 2008 3:10:32 PM
*/
public class PropertiesEx {
/**
*
*/
private static final long serialVersionUID = -6708397622206255544L;
private MonitorThread monitor;
private Properties p;
/**
*
* @param intervalSecond 监听变化间隔
*/
public PropertiesEx(long intervalSecond) {
monitor = MonitorThread.getInstance(intervalSecond);
}
/**
* 默认更新间隔为1s
*/
public PropertiesEx() {
this(1);
}
/**
* 加载配置文件
* @param f
* @throws FileNotFoundException
* @throws IOException
*/
public void load(File f) throws FileNotFoundException, IOException {
p = new Properties();
p.load(new FileInputStream(f));
MonitorEvent event = new FileChangeMonitorEvent(f);
ReloadPropertiesListener listener = new ReloadPropertiesListener();
event.addListener(listener);
monitor.addEvent(event);
if(!monitor.isMonitor()){
monitor.start();
}
}
public String getProperty(String key){
return p.getProperty(key);
}
public Properties getProperties(){
return p;
}
/**
* 当发生属性文件改变时重新加载属性文件<br>
* listener为内部类,为了访问包含类中p成员变量和静止外部访问该类
* @author xuwei
* Jul 30, 2008 3:11:38 PM
*/
private class ReloadPropertiesListener implements IMonitorListener {
public void update(MonitorEvent event) {
FileChangeMonitorEvent fcmEvent = (FileChangeMonitorEvent) event;
try {
p.load(new FileInputStream(fcmEvent.getF()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
3.测试类
package com.hrtc.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import junit.framework.TestCase;
import com.hrtc.monitor.MonitorThread;
public class PropertiesExTest extends TestCase {
protected void setUp() throws Exception {
super.setUp();
}
//测试PropertiesEx中的load方法
public void testLoad() throws FileNotFoundException, IOException {
System.out.println("test reload method========");
PropertiesEx p = new PropertiesEx();
File f = new File(PropertiesTest.class.getResource("").getPath()
+ "test.properties");
p.load(f);
long t1 = System.currentTimeMillis();
int count = 10;
int i = 0;
try {
//循环10秒,在此时间内手动修改配置文件
while (i < count) {
String name = p.getProperty("name");
System.out.println(i + ": name===" + name);
Thread.sleep(1000);
i++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
//测试多个PropertiesEx公用同一MonitorThread
public void testLoadMultiple() throws FileNotFoundException, IOException {
System.out.println("test reload multiple========");
PropertiesEx p1 = new PropertiesEx();
File f1 = new File(PropertiesTest.class.getResource("").getPath()
+ "test.properties");
p1.load(f1);
PropertiesEx p2 = new PropertiesEx();
File f2 = new File(PropertiesTest.class.getResource("").getPath()
+ "test1.properties");
p2.load(f2);
long t1 = System.currentTimeMillis();
int count = 10;
int i = 0;
try {
while (i < count) {
String name = p1.getProperty("name");
String value = p2.getProperty("value");
System.out.println(i + ": " + name + "=" + value);
Thread.sleep(1000);
i++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
//结束监听,清除监听事件
MonitorThread.getInstance().end();
MonitorThread.getInstance().removeAllEvent();
}
}
配置文件内容,与PropertiesExTest放在同一文件夹下
test.properties
name=name1
test1.properties
value=value1
总结
上面的同一个MonitorThread甚至可以用到多个不同的事件中,比如除了Properties外还可以定义如xml等其他事件监听,而只用同一个MonitorThread。另一种方案是使用代理每次访问属性时查看文件是否修改,但是效率上比不上这种。