问题
对于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。另一种方案是使用代理每次访问属性时查看文件是否修改,但是效率上比不上这种。