classloader getresource jar包资源冲突情况,父亲为大,兄弟之间,谁先加载谁牛逼

class /classloader getResourceAsStream()与FileInputStream中,提出了一个问题:

类加载器中各jar包(包括父加载器)内资源冲突时,以谁为准,当时提出一个设想,认为:

父加载器——不一定本jar包(即执行代码的所在jar包)的子加载器所有jar包随机


本文通过实践验证,并得出最佳实践,即为class /classloader getResourceAsStream()与FileInputStream文中所言——

“所以从包名限定的角度说,前者比后者保险,假设2个jar包resource下都有资源文件a,则运行期具体拿哪个依托于类加载器加载顺序,但如果a有个性的包名,则冲突的可能性就大大降低”,当然如果resource中建立多级目录,达到的效果也是一样的

so使用class.getResource instead of class.getClassLoader.getResource

 

案例设计图如下:

*注意,第二个结果“self”有失准确

 

特点:

1 father.conf这个文件3个jar包中都有,文本分别为father、self、brother,以此测试是否会从父加载器先加载资源

2 self.conf这个文件仅有S SB两个同属于一个类加载器jar包有,以此来测试究竟会加载哪个,是否有规律

3 brother.conf这个文件仅有SB有,但会在S的代码中去调用,以此来测试a jar包是否能调用到b jar包的资源,只要双方在同一个类加载器下

 

代码:

public class LcProperty {

    public static String read(String conf) throws IOException {
        System.out.println(LcProperty.class.getClassLoader().getResource(conf));
        InputStream inputStream = LcProperty.class.getClassLoader().getResourceAsStream(conf);
        byte [] bytes = new byte[20];
        inputStream.read(bytes);
        String out = new String(bytes);
        System.out.println(out);
        return out;
    }
}

 

这个类就是图中的S

/*
故意让brother在前面,以此证明即使在sub.jar中调用getresource也不会优先查
sub中的资源或类,而会去查被优先加载的brother.jar
这个加载顺序取决于操作系统
*/
        String dirSub = "file:/Users/sunyuming/Documents/tool/jars//MySub-1.0.0-jar-with-dependencies.jar";
        String dirSubBro = "file:/Users/sunyuming/Documents/tool/jars//MySubBrother-1.0.0-jar-with-dependencies.jar";
        URL urlSub = new URL(dirSub);
        URL urlSubBro = new URL(dirSubBro);

        URLClassLoader cl1 = new URLClassLoader(new URL[]{urlSub, urlSubBro});

        Class c1 = cl1.loadClass("lcproperty.LcProperty");
        Method method1 = c1.getMethod("read", String.class);
        method1.invoke(null, "father.conf");
        method1.invoke(null, "self.conf");
        method1.invoke(null, "brother.conf");


        URLClassLoader cl2 = new URLClassLoader(new URL[]{urlSubBro, urlSub});【重要,放前面的先加载】

        Class c2 = cl2.loadClass("lcproperty.LcProperty");
        Method method2 = c2.getMethod("read", String.class);
        method2.invoke(null, "father.conf");
        method2.invoke(null, "self.conf");
        method2.invoke(null, "brother.conf");

 

这个类是图中的S

输出:

file:/Users/joyce/work/MyTest/MyMain/target/classes/father.conf
father
jar:file:/Users/sunyuming/Documents/tool/jars//MySub-1.0.0-jar-with-dependencies.jar!/self.conf
self
jar:file:/Users/sunyuming/Documents/tool/jars//MySubBrother-1.0.0-jar-with-dependencies.jar!/brother.conf
brother
file:/Users/joyce/work/MyTest/MyMain/target/classes/father.conf
father
jar:file:/Users/sunyuming/Documents/tool/jars//MySubBrother-1.0.0-jar-with-dependencies.jar!/self.conf
brother
jar:file:/Users/sunyuming/Documents/tool/jars//MySubBrother-1.0.0-jar-with-dependencies.jar!/brother.conf
brother

 

结论:

1 father.conf毫无疑问,每次都确定会先到父加载器寻找,从代码看也是如此

    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;
    }

 

2 self.conf 两次输出不一样,取决于类加载器先加载到哪个jar

3 brother.conf ,也毫无疑问的,SB的资源被从S的jar文件中的代码getResource到

 

posted on 2019-11-29 18:34  silyvin  阅读(555)  评论(0)    收藏  举报