美文网首页Android深入
Java加载依赖中properties文件

Java加载依赖中properties文件

作者: 我不吃甜食 | 来源:发表于2018-08-29 10:27 被阅读0次

    properties文件是常用的配置文件类型,本文以properties为例,总结了线上环境常遇到的加载properties文件的问题

    先给一个例子

    项目A:

    maven工程 A

    其中ReadProperties内容如下:

    package com.young.properties;
    
    public class ReadProperties {
        static {
            read();
        }
    
        public ReadProperties(){
            System.out.println("+++++++++++++++");
            readInner();
        }
    
        private void readInner() {
            //方式 1
            InputStream inputStream = this.getClass().getResourceAsStream("/test.properties");
            Properties properties = new Properties();
    
            try {
                properties.load(inputStream);
            } catch (IOException var13) {
            }
    
            System.out.println(properties);
        }
    
        private static void read() {
            //方式 2
            InputStream inputStream = ReadProperties.class.getClassLoader().getResourceAsStream("test.properties");
            Properties properties = new Properties();
    
            try {
                properties.load(inputStream);
            } catch (IOException var13) {
            }
    
            System.out.println(properties);
        }
    }
    

    test.properties 内容如下:

    key=A
    value=2
    
    加载配置文件的方式

    上述方式1方式2是常用的加载方式,使用方式1加载test.properties时,要加上代表根目录的前缀;若以不带"/"的方式加载,如下图显示,实际上是在当前class的路径下寻找,自然是找不到的。
    方式2不能添加"/",默认是在classpath的根路径下寻找文件。

    方式1 不带"/"加载

    项目B:

    另有Maven工程B依赖A
    maven工程 B

    工程B下面也包含了一个同名的test.properties文件。
    test.properties

    key=B
    value=1
    
    public class App {
        public static void main( String[] args ) {
            new ReadProperties();
        }
    }
    

    如果在idea中运行上述main方法,结果是什么呢?可以发现读的是项目B的配置。

    {key=B, value=1}
    +++++++++++++++
    {key=B, value=1}
    

    为什么呢?
    idea里默认的classpath的顺序如下图所示(这些路径下的class都由SystemClassLoader加载),第一个为jdk下的jar,第二个Module source代表target/classes,第三个就是项目A的class;因为项目B的配置在第二个Module source下,所以上面读的是项目B的配置。如果把下图中的相对顺序改变一下,读的配置就会不同(你可以试一下)。


    idea默认的classpath顺序
    线上环境运行

    大家都知道,线上运行代码的方式与编辑器里稍有不同。要么将代码打成fat-jar,要么将所有的依赖单独放在一起(通常是放在单独的文件夹lib中),并将这些依赖放入classpath中。

    fat-jar

    fat jar会将依赖以及配置文件都打包进一个jar文件中,使用maven的朋友可能会使用maven-assembly-plugin来进行打包。把上述工程B打成fat jar目录如下:

    fat-jar
    此时会发现该配置文件test.properties是工程A的配置,工程B的配置被工程A中同名的配置覆盖掉了。因此在开发时,依赖中配置和主项目的配置名称最好不要相同,否则打成fat jar时会出现上述问题。maven-assembly-plugin可能有相关配置可以解决这个覆盖问题,本人并没有找到。
    依赖单独打包

    除了fat jar,通常线上也会采用将依赖放在独立的文件夹中的方式进行部署。
    将上述工程B这样打包后,目录如下:


    image.png

    运行下面的命令

    //读到了B.jar里的配置
    java -classpath B.jar:lib/A.jar  com.young.App
    //读到了A.jar里的配置
    java -classpath lib/A.jar:B.jar com.young.App
    //读到了lib下即A.jar里的配置
    java -Djava.ext.dirs=/.../lib -classpath B.jar com.young.App
    java -cp B.jar -Djava.ext.dirs=/.../lib  com.young.App
    

    前两种都是按照classpath下jar包字典顺序寻找,找到第一个就停止遍历;第三个依赖是由ExtClassLoader加载的,所以jar包的顺序不重要。当com.young.App去加载配置时,先委托给父ClassLoader(ExtClassLoader)加载,所以先加载了A.jar的配置。

    //resource的加载也是双亲委派模型
    public URL getResource(String name) {
            URL url;
            if (parent != null) {
                url = parent.getResource(name);
            } else {
                url = getBootstrapResource(name);
            }
            if (url == null) {
                url = findResource(name);
            }
            return url;
        }
    

    总结

    当项目中多个地方出现相同的配置文件时,读取的文件会与打包的方式以及项目的名称有关。尽量在项目中保持配置文件命名的唯一性。

    相关文章

      网友评论

        本文标题:Java加载依赖中properties文件

        本文链接:https://www.haomeiwen.com/subject/kakfwftx.html