问题
对于java应用中的一些配置文件每次都需要重新启动服务才能重新加载,非常麻烦,故做了一个动态加载资源的程序。
可选方案
使用监听线程监听文件变化,当文件变化时通知程序重新加载配置文件,用到了事件委托模型和观察者模式类似,如下
公共部分
1.Listener接口
- package com.hrtc.monitor;
- /**
- * 监听器接口
- * Jul 30, 2008 3:02:28 PM
- */
- public interface IMonitorListener {
- public void update(MonitorEvent event);
- }
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);
- }
- }
- }
- }
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;
- }
- }
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;
- }
- }
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);
- }
- }
- }
- }
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();
- }
- }
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。另一种方案是使用代理每次访问属性时查看文件是否修改,但是效率上比不上这种。