细数Java项目中用过的配置文件(properties篇)

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

灵魂拷问:在 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<String, PropCache> propCache = new Hashtable<String, PropCache>();

    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. 虽说简单,洋洋洒洒分享一大篇。

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

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

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

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

原文地址:https://www.cnblogs.com/socoool/p/12683501.html

时间: 2024-10-08 15:14:55

细数Java项目中用过的配置文件(properties篇)的相关文章

细数Java项目中用过的配置文件(ini 篇)

Java 菜鸟,会把可变的配置信息写死在代码里:Java 老鸟,会把可变的配置信息提取到配置文件中.坊间流传这么一句非科学的衡量标准,来评判程序员的级别. 那么,项目中的配置信息,你平时都是怎样来实现的呢?你项目中用过哪些配置文件呢? 近期将结合实际项目或源码,说说这些年用过的那些有关配置的奇技淫巧,看看能否帮你提高研发能力(那怕是提高一丢丢,就算成功). 1. 后缀为 .ini 的文件,你用过没? 后缀为 .ini 的文件,是 Initialization File 的缩写,主要用于程序的初始

JAVA Web项目中用到Redis的代码调试

JAVA Web项目中用到Redis的代码调试方法 在项目开发中用到了Redis,需要调试输出数据的正确性, 方法一:修改代码,暂时不从Redis取数据 方法二:清除Redis数据库中的所有key 登录Redis服务器,切换目录到Redis的目录: cd /opt/redis-2.8.13/src/ 清除Redis数据库中的所有key ./redis-cli flushall

Java项目读取配置文件时,找不到指定的文件???

唉,读取个文件,也就是在项目里面去获得配置文件的目录,然后,变成文件,有事没事,总是出个 FileNotFoundException 系统找不到指定的文件,气死人啦.还有就是:System.getProperty("user.dir"),都说获得的是"工作目录",有老铁们在意这个,工作目录怎么理解吗?我这做了个简单的测试.但是,你把这个配置文件放在项目的根目录下面,也就是 说你的项目文件夹下面直接放一个配置文件,这个时候,就可以,啥前缀不加的,就可以读取到,这个配置

Java项目打包在CMD或者Linux下运行

1.在CMD下运行 在eclipse中将项目export成jar包,然后用压缩软件解压 解压后,在CMD中进入项目根目录下,运行程序: Oracle2Redis-jar>java -cp .\lib\*; oracle.xxx.xx.cn.ReadOracle2Redis 2.在Linux中运行 把解压后的文件夹上传到Linux系统中,在项目根目录中运行: java  -cp ./lib/*: oracle.xxx.xx.cn.ReadOracle2Redis (跟Windows下路径分隔符(\

Java学习-007-Log4J 日志记录配置文件详解及实例源代码

此文主要讲述在初学 Java 时,常用的 Log4J 日志记录配置文件详解及实例源代码整理.希望能对初学 Java 编程的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激!源代码测试通过日期为:2015-1-30 13:54:02,请知悉. 所需的 jar 包下载链接为:http://yunpan.cn/cKE56sxqtQCfP  访问密码 63d8 有关 Log4J 日志文件中日志级别及文件配置的详细情况,在 Log4J 的配置文件(xml.properties)中有详细的介绍,敬请参阅!

java项目——数据结构实验报告

java项目——数据结构总结报告 20135315  宋宸宁 实验要求 1.用java语言实现数据结构中的线性表.哈希表.树.图.队列.堆栈.排序查找算法的类. 2.设计集合框架,使用泛型实现各类. 3.API的编写,并导出. 4.使用TDD模式,对程序进行测试,利用TestSuite将各测试类整合到一起. 5.与小组成员实现代码的整合. 实验设计过程 首先自学集合框架章节的内容,初步设计相关的类. 根据数据结构课本的章节分类,实验各数据结构类. 在类的编写过程中,经过老师的指导,我准备使用泛型

java项目测试log4j

.literal { background-color: #f2f2f2; border: 1px solid #cccccc; padding: 1px 3px 0; white-space: nowrap; color: #6d180b; font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 16px } 一.基础入门 1.1 下载安装 下载相应的log4j-1.2.15.jar(也可以下载

细数空格缩进在开发中的坑

没错,这是一篇火药文.网上流传的各种编程规范几乎青一色都是说用空格代替Tab,其理由几乎都是“这是唯一能保证在所有环境下获得一致展现的方法”. 那我想问一句,你们到底能要多少种环境要看代码??? 1.用Windows自带的记事本?那我只能说,你继续装,看你什么时候才能完成得了任务.2.连接到Linux服务器上使用VI或者VIM?我也只能说只有改服务器的配置文件的时候才会这样做吧,而且如果是配置文件比较复杂也是下载下来用Notepad++改清晰很多好伐,比如Aapche的配置文件在Notepad+

Gradle在大型Java项目上的应用

在Java构建工具的世界里,先有了Ant,然后有了Maven.Maven的CoC[1].依赖管理以及项目构建规则重用性等特点,让Maven几乎成为Java构建工具的事实标准.然而,冗余的依赖管理配置.复杂并且难以扩展的构建生命周期,都成为使用Maven的困扰. Gradle作为新的构建工具,获得了2010 Springy大奖,并入围了2011的Jax最佳Java技术发明奖.它是基于Groovy语言的构建工具,既保持了Maven的优点,又通过使用Groovy定义的DSL[2],克服了 Maven中