java不重启服务动态加载properties文件

一般情况下,配置文件是系统在启动时加载的,当配置文件发生改动后需要重新启动系统。但是,在多数业务里,需要动态加载配置文件已经成为了标配,那如何实现动态加载呢?我们一一列举。

一、archaius

archaius是Netflix公司开源项目之一,基于java的配置管理类库,主要用于多配置存储的动态获取。主要功能是对apache common configuration类库的扩展。在云平台开发中可以将其用作分布式配置管理依赖构件。同时,它有如下一些特性:

  • 动态获取属性
  • 高效和线程安全的配置操作
  • 配置改变时提供回调机制
  • 可以通过jmx操作配置
  • 复合配置

示例:

<dependency>
  <groupId>com.netflix.archaius</groupId>
  <artifactId>archaius-core</artifactId>
  <version>0.7.4</version>
</dependency>

public class Main {
    //获取一个Long型的动态配置项,默认值是1000。
    private static DynamicLongProperty timeToWait =
            DynamicPropertyFactory.getInstance().getLongProperty("lock.waitTime", 1000);

    public static void main(String[] args) throws Exception {
        //设置回调
        timeToWait.addCallback(() -> {
            System.out.println("timeToWait callback, new value: " + timeToWait.get());
        });
        //每秒将timeToWait动态配置值打印到控制台,timeToWait.get()会动态的获取最新的配置
        for (int i = 0; i < 100000; i++) {
            Thread.sleep(1000);
            System.out.println("timeToWait: " + timeToWait.get());
        }
    }
}

archaius默认读取classpath下的config.properties,所以在resources目录下增加config.properties文件,文件中增加配置:
lock.waitTime=4

修改config.properties后,默认等1分钟后会生效,可以通过jvm参数修改:

-Darchaius.fixedDelayPollingScheduler.delayMills=2000 -Darchaius.fixedDelayPollingScheduler.initialDelayMills=2000

上述是读取Long型数据,同样还有:DynamicStringProperty、DynamicDoubleProperty...

详情参考:http://techblog.ppdai.com/2018/05/08/20180508/

二、PropertiesConfiguration

PropertiesConfiguration是Apache开元的一个配置文件的加载工具类,如下:

<dependency>
	<groupId>commons-configuration</groupId>
	<artifactId>commons-configuration</artifactId>
	<version>1.6</version>
</dependency>

public class PropertyUtil {
	private PropertyUtil() {
	};

	private static class SingletonHolder {
		private static PropertiesConfiguration propertyUtil = init("config.properties");
	}

	public static PropertiesConfiguration getProps() {
		return SingletonHolder.propertyUtil;
	}

	/**
	 * 初始化
	 * 
	 * @param propertiesFile
	 * @see
	 */
	private static PropertiesConfiguration init(String propertiesFile) {
		PropertiesConfiguration PropertyUtil = null;
		try {
			PropertyUtil = new PropertiesConfiguration(propertiesFile);
			// 自动重新加载
			PropertyUtil
					.setReloadingStrategy(new FileChangedReloadingStrategy());
			// 自动保存
			PropertyUtil.setAutoSave(true);
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
		return PropertyUtil;
	}

	/**
	 * 根据Key获得对应的value
	 *
	 * @param key
	 * @return
	 * @see
	 */
	public static Object getProperty(String key) {
		return getProps().getProperty(key);
	}

	/**
	 * 设置属性
	 *
	 * @param key
	 * @param value
	 * @see
	 */
	public static void setProperty(String key, String value) {
		getProps().setProperty(key, value);
	}
}

测试:

public static void main(String...strings) throws InterruptedException {
		for (int i = 0; i < 100000; i++) {
            Thread.sleep(1000);
            System.out.println("timeToWait: " + PropertyUtil.getProperty("lock.waitTime"));
        }
}

修改配置文件后立即生效。

参考:https://blog.52itstyle.vip/archives/885/

三、WatchService JDK7+

该类的对象就是操作系统原生的文件系统监控器!我们都知道OS自己的文件系统监控器可以监控系统上所有文件的变化,这种监控是无需遍历、无需比较的,是一种基于信号收发的监控,因此效率一定是最高的。现在Java对其进行了包装,可以直接在Java程序中使用OS的文件系统监控器了。

1、ResourceListener :

public class ResourceListener {
    
    private static ExecutorService fixedThreadPool = Executors
            .newFixedThreadPool(5);
    private WatchService ws;
    private String listenerPath;
    
    private ResourceListener(String path) {
        try {
            ws = FileSystems.getDefault().newWatchService();
            this.listenerPath = path;
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void start() {
        fixedThreadPool.execute(new Listner(ws, this.listenerPath));
    }

    public static void addListener(String path) throws IOException {
        ResourceListener resourceListener = new ResourceListener(path);
        Path p = Paths.get(path);
        p.register(resourceListener.ws, StandardWatchEventKinds.ENTRY_MODIFY,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_CREATE);
    }

    public static void main(String[] args) throws IOException {
        ResourceListener.addListener("E:/test");
    }
}

class Listner implements Runnable {
    private WatchService service;
    private String rootPath;

    public Listner(WatchService service, String rootPath) {
        this.service = service;
        this.rootPath = rootPath;
    }

    public void run() {
        try {
            while (true) {
                WatchKey watchKey = service.take();
                List<WatchEvent<?>> watchEvents = watchKey.pollEvents();
                for (WatchEvent<?> event : watchEvents) {
                    String path = event.context().toString();
                    if(path.endsWith("config.properties")){
                        LogUtil.info("[" + rootPath + "/" + event.context()+ "]文件发生了[" + event.kind() + "]事件");
                        PropertyUtil.init();//配置变更初始化
                    }
                }
                watchKey.reset();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                service.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2、工具类 PropertyUtil.java

public class PropertyUtil {
    private PropertyUtil(){};
    
    private static class SingletonHolder{
        private static Properties props  = loadProps();
    }
    public static Properties getProps(){
        return SingletonHolder.props;
    }
    private static  Properties loadProps(){
        Properties props = new Properties();
        InputStream in = null;
        try {
            in = PropertyUtil.class.getClassLoader().getResourceAsStream("config.properties");
            props.load(in);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(null != in) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return props;
    }

    public static String getProperty(String key){
        return getProps().getProperty(key);
    }

    public static String getProperty(String key, String defaultValue) {
        return getProps().getProperty(key, defaultValue);
    }
    public static void init(){//不要随便调用(不是私有的)
        SingletonHolder.props =  loadProps();
    }
}

参考:https://blog.52itstyle.vip/archives/885/

四、手写PropertiesUtils:

使用Properties时,每次get数据进行文件判断。

public class PropertiesUtil {
	private static Properties prop;
	private static Long lastModified = 0L;

	/**
	 * 加载文件
	 */
	private static void init(String configFile) {
		prop = new Properties();
		String filepath = new File(configFile).getPath();
		try {
			prop.load(PropertiesUtil.class.getClassLoader().getResourceAsStream(configFile.trim()));
			//prop.load(new FileInputStream(configFile.trim()));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			File f = new File(filepath);
			lastModified = f.lastModified();
		}
	}

	private static boolean isPropertiesModified(String configFile) {
		boolean returnValue = false;
		File file = new File(PropertiesUtil.class.getClassLoader().getResource(configFile).getPath());
		//File file = new File(configFile);
		if (file.lastModified() > lastModified) {
			lastModified = file.lastModified();
			returnValue = true;
		}
		return returnValue;
	}
	
	public static String get(String configFile,String key) {

        if (prop == null || isPropertiesModified(configFile)) {
            init(configFile);
        }
        String value = prop.get(key).toString();
        return value;
    }
}

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值