灵魂拷问:在不重启服务的前提下,如何让配置修改生效的呢?有什么奇技淫巧吗?

灵魂拷问:在 Java 项目中,总能看到以 .properties 为后缀的文件踪影,这类配置文件是怎么加载的呢?

项目研发过程中,总会遇到一些经常改变的参数,比如要连接的数据库的连接地址、名称、用户名、密码;再比如访问三方服务的 URL 等等。考虑到程序的通用性,这些参数往往不能直接写死在程序里,通常借助配置文件来优雅处理。

在 Java 项目中,properties 文件当属使用较简单一类,不过虽然简单,还是要好好说说项目中都是怎么使用的,尝试通过源码解读,让你真正懂它,并带你深刻体会 Java 中重载的意义。

1. 虽说简单,格式还是要看一看。

相比上次谈及的 ini 配置文件,properties 文件格式没有 Section (节)的概念,反而是简单了不少。

上图是一个 jdbc 连接所需要的配置,其中以 # 开始的每一行是注释信息,而以等号分割的每行配置,就是常说的键-值对,等号左边的为 key(代码中的变量),等号右边的为 value(是依据实际场景而配置的值)。

2. 虽说简单,Java 源码还是去要看看。

在 Java 中提供了 java.util.Properties 类,主要用于对配置文件的读写操作。

一图掌握血缘关系,很显然 Properties 继承自 Hashtable,归根结底是个 Map,而 Properties 最特殊的地方,就是它的键和值都是字符串类型。

从全局了解梗概,然后走进 JDK 源码,按照思路,步步去深入。

首先,要看看写好的配置文件是怎么加载的?

源码很清晰,提供字符流 Reader、字节流 InputStream两种方式加载配置文件(方法重载的目的:让使用者更方便),再深入去看最终会调用 Hashtable 的 put(key, value) 来设置键值对,最终完成配置文件中的加载。

然后,要看看怎么根据 key 获得对应的 value(放进去了,还要考虑拿出来)?

源码很清晰,通过参数 key 获得对应的 value,考虑到使用者的方便,对 getProperty 方法进行了重载,其中标注 2 的方法,当根据 key 获得值是 null 时,会返回一个调用方法时传入的默认值(很多场景下,确实很有用)。

知道了怎么加载配置文件,知道了怎么获取 key 对应的值,按照常理说,项目中已经够用了,但是有些时候项目启动后,还真需要再额外设置一下参数的值,不过没关系,因为 Java 已经想到了这一点,对外提供了 setProperty 的方法,让额外设置参数成为可能。

若能在项目研发中,熟练使用上面提到的这些 API,已经足矣。既然打开了源码,索性把杂七杂八的都提提,说不定某些 API 也能解决你碰到的其它场景的问题呢。

Properties 类不仅提供了 load 进行加载配置,而且还提供了把键值对写到文件中的能力。如上面源码所示,考虑到使用者的方便,对 store 方法进行重载,提供了面向字节流 OutputStream、字符流 Writer 两种方式的 store。

如上面源码所示,Properties 除了提供对常规配置文件读写能力支撑,对 xml 配置文件加载、写入也提供了支撑。

如上图源码所示,Properties 类提供了重载的 list 方法,为了方便调试,可以把键值对列表给整齐的打印出来。

3. 虽说简单,不能赋予实践一切都是扯淡。

场景一:在 APM 性能监控时,获取 Java 应用画像信息常用 API。

import java.util.Properties;

/**

* @author 一猿小讲

*/

public class JVMDetails {

public static void main(String[] args) {

// 获取系统信息(JDK信息、Java虚拟机信息、Java提供商信息、当前计算机用户信息)

Properties properties = System.getProperties();

// 把系统信息打印一下

properties.list(System.out);

}

}

程序跑起来,部分输出截图示意如下。

场景二:业务开发中,让配置替代硬编码,并考虑配置更新时,程序能够读到最新的值。

import java.io.*;

import java.util.Hashtable;

import java.util.Properties;

/**

* 配置文件工具类

* 1. 支持加载 .properties文件、.ini文件

* 2. 支持配置文件更新

* @author 一猿小讲

*/

public class PropertiesUtil {

// private static final Log4j LOG = .....;

private static Hashtable propCache = new Hashtable();

public static String getString(String propFile, String key) {

return getString(propFile, key, null);

}

public static String getString(String propFile, String key, String defaultValue) {

if ((!propFile.endsWith(".properties")) && (!propFile.endsWith(".ini"))) {

propFile = propFile + ".properties";

}

PropCache prop = propCache.get(propFile);

if (prop == null) {

try {

prop = new PropCache(propFile);

} catch (IOException e) {

// LOG.warn(e);

System.out.println(String.format("读取 %s 出现异常%s", propFile,e));

return defaultValue;

}

propCache.put(propFile, prop);

}

String value = prop.getProperty(key, defaultValue);

if (value != null) {

value = value.trim();

}

return value;

}

public static void setString(String propFile, String key, String value)

throws IOException {

if ((!propFile.endsWith(".properties")) && (!propFile.endsWith(".ini"))) {

propFile = propFile + ".properties";

}

File file = new File(propFile);

Properties prop = new Properties();

try {

FileInputStream fis = new FileInputStream(file);

prop.load(fis);

fis.close();

} catch (FileNotFoundException e) {

// LOG.warn(e);

System.out.println(String.format("文件 %s 不存在", propFile));

}

FileOutputStream fos = new FileOutputStream(file);

prop.put(key, value);

prop.store(fos, null);

fos.close();

PropCache localPropCache = propCache.get(propFile);

if (localPropCache != null) {

localPropCache.reload();

}

}

private static class PropCache {

private String fileName;

private long lastLoad;

private Properties prop;

public PropCache(String propFileName) throws IOException {

File file = new File(propFileName);

FileInputStream fis = new FileInputStream(file);

this.prop = new Properties();

this.prop.load(fis);

fis.close();

this.fileName = file.getAbsolutePath();

this.lastLoad = file.lastModified();

}

public String getProperty(String key, String defaultValue) {

File file = new File(this.fileName);

if (this.lastLoad < file.lastModified()) {

reload();

}

return this.prop.getProperty(key, defaultValue);

}

public void reload() {

File file = new File(this.fileName);

try {

Properties prop = new Properties();

FileInputStream fis = new FileInputStream(file);

prop.load(fis);

fis.close();

this.prop.clear();

this.prop.putAll(prop);

this.lastLoad = file.lastModified();

} catch (IOException e) {

// PropertiesUtil.LOG.warn(e);

System.out.println(String.format("文件 %s 重新加载出现异常%s", this.fileName, e));

}

// PropertiesUtil.LOG.all(new Object[]{this.fileName, " reloaded."});

System.out.println(String.format("文件 %s 重新加载完毕", this.fileName));

}

}

}

借助开篇提到的 jdbc 配置文件,进行验证。尝试获取数据库类型,默认配置为 db2,中途修改参数的值为 mysql,看看效果如何?

/**

* 测试类

* @author 一猿小讲

*/

public class M {

public static void main(String[] args) {

while(true) {

System.out.println(PropertiesUtil.getString("db", "jdbc.type"));

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

}

}

}

}

程序跑起来,效果还是让人很满意。

4. 虽说简单,洋洋洒洒分享一大篇。

有关配置文件的分享网上有很多,而我们的分享却显得不太一样。

我们的初衷是:结合实际项目及源码,说说这些年用过的那些有关配置的奇技淫巧,帮你提高研发能力(那怕是提高一丢丢,就算成功)。

它山之石可以攻玉,相信会对你有所帮助。

为了能够帮你提高研发能力(那怕是提高一丢丢呢),后续将继续结合实际项目,看看用到的其它形式的配置文件,敬请期待。

java项目 配置文件_细数Java项目中用过的配置文件(properties篇)相关推荐

  1. c读取ini配置文件_细数Java项目中用过的配置文件(ini 篇)

    Java 菜鸟,会把可变的配置信息写死在代码里:Java 老鸟,会把可变的配置信息提取到配置文件中.坊间流传这么一句非科学的衡量标准,来评判程序员的级别. 那么,项目中的配置信息,你平时都是怎样来实现 ...

  2. java 短路判断_细数Java最坑爹的10大功能点

    作者:践行精神自由 原文:https://www.sohu.com/a/357927861_115128 作为一门面向对象的编程语言,Java凭借其简单易用.功能强大的特点受到了广大编程爱好者的青睐, ...

  3. 不允许使用java方式启动_细品 Java 中启动线程的正确和错误方式

    细品 Java 中启动线程的正确和错误方式 前文回顾详细分析 Java 中实现多线程的方法有几种?(从本质上出发) start 方法和 run 方法的比较 代码演示:/** * * start() 和 ...

  4. 视频教程-大型Java项目视频教程_王勇老师DRP项目教程完整版292集-Java

    大型Java项目视频教程_王勇老师DRP项目教程完整版292集 动力节点王勇老师,CCTV<影响力对话>栏目特约嘉宾,Java培训知名讲师,中国Java培训领军人物,北京动力节点创始人,董 ...

  5. php反序列化java.long_细数java中Long与Integer比较容易犯的错误总结

    今天使用findbugs扫描项目后发现很多高危漏洞,其中非常常见的一个是比较两个Long或Integer时直接使用的==来比较. 其实这样是错误的. 因为Long与Ineger都是包装类型,是对象. ...

  6. 《八股文》细数Java线程、并发、锁,温故而知新

    <八股文>细数Java线程.并发.锁,温故而知新 基础 1. 并行.并发有什么区别? 2. 说说什么是进程和线程? 3. 说说线程有几种创建方式? 4. 为什么调用start()方法时会执 ...

  7. yaml for java_细数Java项目中用过的配置文件(YAML篇)

    灵魂拷问:YAML,在项目中用过没?它与 properties 文件啥区别? 目前 SpringBoot.SpringCloud.Docker 等各大项目.各大组件,在使用过程中几乎都能看到 YAML ...

  8. java ee论文_基于JavaEE的项目管理系统毕业论文.doc

    基于JavaEE的项目管理系统毕业论文 基于JavaEE的项目管理系统的设计与实现 摘 要 企业项目管理系统是为了使企业项目能够按照预定的成本.进度.质量顺利完成,而对人员.产品.过程和项目进行分析和 ...

  9. jit java 怎么配置_新的Java JIT编译器Graal简介

    在本教程中,我们将深入研究名为Graal的新Java实时(JIT)编译器. 让我们首先解释JIT编译器的作用. 当我们编译Java程序时(例如,使用  javac命令),我们最终将源代码编译成代码的二 ...

最新文章

  1. WMI技术介绍和应用——查询系统信息
  2. JAVA实现矩形覆盖问题(《剑指offer》)
  3. 2.Java异常学习
  4. 深入Garbage First垃圾收集器(三)G1中的垃圾收集
  5. altium designer2020中文版
  6. spring cloud的eureka.client.service-url.defaultZone配置eureka集群的写法
  7. python数码时钟代码_python时钟的实现
  8. Spark学习之RDD的概念
  9. UWP 手绘视频创作工具技术分享系列 - 位图的绘制
  10. 远程连接linux的mysql_【Linux开启mysql远程连接的设置步骤】 mysql开启远程连接
  11. intllij idea 快捷键 Mac
  12. raid5用户mbr还是gpt_系统硬盘gpt转换的操作方法
  13. 为什么不使用Go语言呢?
  14. 计算经纬度、距离、方位角
  15. 淘宝返利是怎么回事?是真的还是假的?
  16. java ico_Java 读写 ICO 图像
  17. 讯为4412蜂鸣器驱动实现
  18. 解决git中upstream丢失问题Your branch is based on 'origin/xxxx', but the upstream is gone.
  19. 如何快速将手写数据录入 Excel
  20. 计算机台式机硬盘,台式电脑硬盘和笔记本硬盘有什么区别【详解】

热门文章

  1. 如何对SAP Spartacus支持路由的Component进行单元测试
  2. SAP ABAP里存在Java List这种集合工具类么?CL_OBJECT_COLLECTION了解一下
  3. function implemented in Scala - compiled java code - some closure example
  4. when is odata request sent for Live report in SAP CRM
  5. how does SAP ui5 know the phone, tablet type, os type
  6. regular expression in SAP jam integration
  7. why COMM_PRFREEATTR could not appear in F4 help
  8. React的source code init时会自动检测Chrome dev tool的react extension装了没
  9. Employee Assign Organization unit
  10. 使用代码创建SAP Sales area