package com.umpay.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
/** 动态配置文件
* @author liwei2@umpay.com
* @since 2007-12-16
* 用JProfiler观察可以看出只使用了一个监视线程
* String fn1 = "config/test111.properties";
* String fn2 = "config/test222.properties";
* DynamicProperties dp1 = new DynamicProperties(fn1,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
* DynamicProperties dp2 = new DynamicProperties(fn2,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
* while(true) {
* System.err.println("#1="+dp1.getProperty("key"));
* System.err.println("#2="+dp2.getProperty("key"));
* Thread.sleep(1000*5);
* }
*
* */
public final class DynamicProperties {
/** 动态配置文件磁盘数据*/
protected File file;
/** 动态配置文件内存数据( {@link DynamicProperties} 的目的就是保持从磁盘数据到内存数据的单向一致性,
* 暂不支持从内存到磁盘的数据同步,因此{@link DynamicProperties}不提供set方法,只提供get方法.*/
protected Properties property;
/** 动态检测相关参数:第一次检测前的延迟时间,单位ms*/
protected long delay;
/** 动态检测相关参数:检测周期,单位ms*/
protected long period;
/** 动态检测相关参数:{@link FileMonitor} 最后检测时间,单位ms*/
protected long lastMonitorTime;
public String getFileName() {
return file.getName();
}
long getLastModified() {
return this.file.lastModified();
}
public long getDelay() {
return delay;
}
public long getPeriod() {
return period;
}
long getLastMonitorTime() {
return this.lastMonitorTime;
}
void setLastMonitorTime(long lastMonitorTime) {
this.lastMonitorTime = lastMonitorTime;
}
/**
* 所有 {@link DynamicProperties} 实例共享一个 {@link FileMonitor}
* */
private static FileMonitor monitor = null;
private synchronized static void initFileMonitor() {
if(monitor == null) {
monitor = new FileMonitor();
}
}
/**
* @param file 属性文件
* @param delay 从<code>DynamicProperties</code>被创建到第一次动态监视的时间间隔. 约束范围delay > 0
* @param period 动态监视的时间间隔. 约束范围period >= 0;等于0表示不执行动态监视,退化为静态配置文件.
*
* */
public DynamicProperties(File file,long delay,long period)
throws IOException
{
if(delay < 0 || period < 0) {
throw new IllegalArgumentException("参数delay和period都必须大于等于0");
}
this.file = file;
this.delay = delay;
this.period = period;
this.property = new Properties();
this.initAndLoad();//初始构造时,执行第一次加载.
}
public DynamicProperties(String fileName,long delay,long period)
throws IOException
{
this(new File(fileName),delay,period);
}
public DynamicProperties(File file,Date firstTime,long period)
throws IOException
{
this(file,firstTime.getTime()-System.currentTimeMillis(),period);
}
public DynamicProperties(String fileName,Date firstTime,long period)
throws IOException
{
this(new File(fileName),firstTime.getTime()-System.currentTimeMillis(),period);
}
public DynamicProperties(File file,long period)
throws IOException
{
this(file,0,period);
}
public DynamicProperties(String fileName,long period)
throws IOException
{
this(new File(fileName),period);
}
private void initAndLoad() throws IOException {
this.lastMonitorTime = System.currentTimeMillis();
update();//首次将配置信息从文件加载到内存
if(period > 0) {//如果period=0,则表示静态配置文件,不需要进行动态更新的监视
initFileMonitor();
monitor.addDetected(this);//启动FileMonitor,以监测磁盘文件内容的变化,并在变化时,由监视线程回调update()方法,进行重新加载
}
}
/**
* {@link FileMonitor} 线程回调 {@link #update()} 方法,一定会在{@link #initAndLoad()}之后,
* 所以尽管 {@link #update()}方法会被两个线程执行,一个是构造 {@link DynamicProperties}实例所在的线程,
* 另一个是 {@link FileMonitor}线程,但是它们是顺序执行的,实例构造完成后,只有 {@link FileMonitor}
* 线程执行 {@link #update()}方法。因此 {@link #update()}不同担心线程安全的问题.
* */
void update() throws IOException {
InputStream in = new FileInputStream(file);
this.property.load(in);
}
public String getProperty(String key, String defaultValue) {
String val = this.property.getProperty(key);
return val == null ? defaultValue : val.trim();
}
public String getProperty(String key) {
String val = this.property.getProperty(key);
return val == null ? null : val.trim();
}
public boolean getBoolean(String key) {
String val = this.getProperty(key);
return Boolean.parseBoolean(val);
}
public boolean getBoolean(String key,boolean defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Boolean.parseBoolean(val);
}
public int getInt(String key) {
String val = this.getProperty(key);
return Integer.parseInt(val);
}
public int getInt(String key,int defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Integer.parseInt(val);
}
public double getDouble(String key) {
String val = this.getProperty(key);
return Double.parseDouble(val);
}
public double getDouble(String key,double defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Double.parseDouble(val);
}
/* 为了方便使用,避免对象实例在各个类中传递,提供些静态方法*/
public static final void initInstance(String instanceName,DynamicProperties instance) {
if(instanceName == null || instance == null) {
throw new IllegalArgumentException("参数不能为空");
}
synchronized (context) {
if(context.containsKey(instanceName)) {
throw new IllegalStateException("名称为"+instanceName+"的实例已经存在");
}
context.put(instanceName, instance);
}
}
public static final DynamicProperties getInstance(String instanceName) {
synchronized (context) {
return (DynamicProperties)context.get(instanceName);
}
}
private static final String DEFAULT = DynamicProperties.class.getName()+"#DEFAULT";
public static final void initDefaultInstance(String fileName,long delay,long period)
{
try {
initInstance(DEFAULT, new DynamicProperties(fileName,delay,period));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public static final DynamicProperties getDefaultInstance() {
DynamicProperties dp = getInstance(DEFAULT);
if(dp == null) {
throw new IllegalStateException("默认实例尚未初始化,请用initDefaultInstance方法进行初始化");
}
return dp;
}
private static Map context = new HashMap();//Collections.synchronizedMap(new HashMap());
/** 小工具:将应用程序的命令行参数转化成 {@link Properties}*/
public static Properties parseArgs(String[] args) {
Properties config = new Properties();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(baos);
for (int i = 0; i < args.length; i++) {
writer.println(args[i]);
}
writer.flush();
try {
config.load(new ByteArrayInputStream(baos.toByteArray()));
} catch (IOException e) {
throw new IllegalStateException(e);
}
return config;
}
public static void main(String[] args) throws Exception {
//用JProfiler观察可以看出只使用了一个监视线程
String fn1 = "config/test111.properties";
String fn2 = "config/test222.properties";
DynamicProperties dp1 = new DynamicProperties(fn1,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
DynamicProperties dp2 = new DynamicProperties(fn2,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
DynamicProperties.initDefaultInstance("config/test.properties", 0, 20);
while(true) {
System.out.println("#1="+dp1.getProperty("key"));
System.out.println("#2="+dp2.getProperty("key"));
System.out.println("#Default="+DynamicProperties.getDefaultInstance().getProperty("key"));
Thread.sleep(1000*5);
}
}
}
class FileMonitor {
private Timer timer = new Timer("DynamicPropertiesTimer");
public synchronized void addDetected(final DynamicProperties detected) {
if(detected.getDelay() < 0) ;//if (delay < 0) delay = 0L;
if(detected.getPeriod() <= 0) return;
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
long t = detected.getLastModified();
if(t > detected.getLastMonitorTime()) {
detected.setLastMonitorTime(t);
try {
detected.update();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
}, detected.getDelay(), detected.getPeriod());
}
}
分享到:
相关推荐
在VC++(Visual C++)开发环境中,读取动态配置文件是常见的需求,尤其是在软件设计中需要根据用户需求或环境变化灵活调整程序行为时。动态配置文件通常采用文本格式,如INI、XML或JSON,方便非程序员进行编辑。本...
本文将深入探讨如何在Java中实现动态修改配置文件,同时解决中文字符编码问题,使得配置文件的读写更加高效和便捷。 首先,我们需要理解Java中的Properties类,它是处理配置文件的标准工具。`java.util.Properties`...
在Spring框架中,动态加载配置文件是一项重要的功能,它使得开发者在开发过程中无需重启应用就能实时更新配置,极大地提高了开发效率。热部署方案是这一功能的具体应用,它允许我们在不中断服务的情况下,对应用程序...
xml放在tomcat的conf下面的localhost文件夹下,不用再每次部署项目
iOS动态化配置页面(后端或用户可控制页面的样式布局和自定义编辑) 适用场景: 1)页面需要运营频繁调整的,比如那些东西需要展示,那些东西需要隐藏,那些模块位置需要调整等等 2)类似淘宝店铺可用户自己编辑页面...
本知识点将深入探讨如何在C# WinForm应用中通过配置文件来实现DLL的动态加载。 首先,我们需要理解配置文件的作用。配置文件(如app.config或web.config)用于存储应用程序的设置,如数据库连接字符串、服务端口等...
"Kaddu:动态配置文件管理器"是一个专为简化配置文件管理而设计的工具,尤其适用于Java开发环境。在软件开发过程中,配置文件是不可或缺的一部分,它们存储了应用程序运行时所需的参数和设置。Kaddu的出现旨在解决...
7. **动态配置** 有些应用程序支持动态加载或更新配置,无需重启服务。这通常通过监听配置文件的变更事件或提供API接口来实现,允许在运行时调整配置。 8. **配置文件的最佳实践** - 保持简洁:避免不必要的复杂...
星空-动态配置文件管理器 TL; DR:自动部署点文件。 分组为具有动态行为的模块。 它有什么作用? 受和启发,Astrality是用于管理配置文件的灵活工具。 让我们从Astrality的一些主要功能列表开始: 根据中央YAML...
标题“从配置文件动态创建菜单”揭示了我们讨论的主题,即通过读取配置文件(可能是JSON、XML或YAML格式)来构建应用的菜单结构。这样的设计模式有诸多优点,比如灵活性、可扩展性和易于维护性。 描述“从配置文件...
- 考虑使用配置管理服务,如Netflix的Archaius或Spring Cloud Config,提供动态配置更新能力。 综上所述,"配置文件jar包"是一个整合了配置文件和相关库的打包解决方案,它在软件部署和运行中起着至关重要的作用,...
Apache Commons Configuration支持动态加载配置文件,这意味着当配置文件发生改变时,程序无需重启就能感知到这些变化。这主要通过监听配置文件的变化来实现。例如,可以使用`FileChangedReloadingStrategy`类监听...
修改配置文件的过程稍微复杂些,因为.NET框架并不直接支持动态更新`appSettings`。你需要先创建一个`Configuration`对象,然后通过该对象修改`appSettings`。以下是修改`Setting1`值的步骤: - 加载配置文件: `...
7. **配置文件的动态加载** - 在运行时,如果配置文件发生更改,可以使用`spring.cloud.config.server.git.auto-refresh`配置项来实现动态刷新。 8. **使用Config Server** - 当项目规模进一步扩大时,可以考虑...
下面将详细介绍如何在C# WinForm程序中通过配置文件实现DLL的动态加载。 首先,我们需要理解什么是DLL(Dynamic Link Library)。DLL是一种可执行文件格式,它包含可由多个程序同时使用的代码和数据。在C#中,我们...
本篇文章将深入探讨如何根据配置文件动态生成RabbitMQ的队列,并创建交换器以及相应的绑定关系。 首先,我们需要了解RabbitMQ的基本概念。队列(Queue)是消息的存储位置,交换器(Exchange)负责根据预设的规则...
这些信息往往需要根据不同的环境或需求进行调整,而动态修改配置文件则能提高效率,避免手动编辑文件带来的错误风险。 PHP中的正则表达式(Regular Expression)是一种强大的文本处理工具,可以用于查找、替换或者...
在Spring Boot 2.7.6中,配置文件的管理是整个框架的核心功能之一,它使得开发者能够方便地配置和管理应用的属性。本篇文章将详细介绍Spring Boot如何读取配置文件,以及提供一些调试示例代码,帮助你更好地理解和...
配置文件通常用于存储应用的设置、参数或特定环境的信息,以便程序在运行时能够动态调整行为。本篇文章将探讨如何通过宏来简化配置文件的处理,提高代码的可读性和可维护性。 首先,我们关注"ConfigBase.h"这个...