class.getResource和ClassLoader.getResource的区别和分析

前言

  在项目中通过Java的Api获取资源的场景各种各样,总会离不开获取项目的配置文件,而class.getResource和ClassLoader.getResource作为获取资源的常用手段,所以对它们需要有一定的了解,才能正确运用它们。

class.getResource和ClassLoader.getResource

  前者通过调用后者的Api来获取资源,搜索路径则由父级ClassLoader决定,通过Javadoc得知:

1
2
class.getResource查找与给定类相关的资源的规则是通过定义类的 ClassLoader 实现的。   
ClassLoader.getResource首先搜索资源的父类加载器;如果父类加载器为 null,则搜索的路径就是虚拟机的内置类加载器的路径。如果搜索失败,则此方法将调用 findResource(String) 来查找资源。
  • class.getResource:父类加载器是App ClassLoader

    • class.getResource(“bar.txt”):获取的资源是该类的包路径下的bar.txt
    • class.getResource(“/bar.txt”):获取的资源是classpath根路径下的bar.txt
  • ClassLoader.getResource:

    • ClassLoader.getResource(“bar.txt”):获取的资源是classpath根路径下的bar.txt
    • ClassLoader.getResource(“/bar.txt”):获取的资源为null值,这是因为ClassLoader的父加载器为Boot ClassLoader,这个类加载器是最底层的加载器,所以加载路径为null

通过getResource的源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  public java.net.URL getResource(String name)
    {
//解析传入的参数
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null)
        {
            // A system class.
            return ClassLoader.getSystemResource(name);
        }
        return cl.getResource(name);
    }
//getResource的关键点是在于传入的参数,先通过resolveName解析处理参数
private String resolveName(String name)
    {
        if (name == null)
        {
            return name;
        }
        if (!name.startsWith("/"))
        {
            Class c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1)
            {
//class.getResource获取类包路径
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        }
else {
//如果是以"/"开头,则去掉
            name = name.substring(1);
        }
        return name;
    }

所以无论是使用class.getResource还是ClassLoader.getResource,需要注意传入参数是否以”/“开头,确保资源能正确被获取。

其它路径

1
2
3
4
5
6
//返回当前运行Java Application路径
System.getProperty("user.dir");
//返回用户根目录路径
System.getProperty("user.home");
//获取jar包或者classes根目录所在路径:类似/xx/.../xx.jar或者/xx/.../classes
class.getProtectionDomain().getCodeSource().getLocation();

参考资料

  1. 深入分析Java ClassLoader原理
  2. Class.getResource和ClassLoader.getResource的区别分析
  3. Different ways of loading a file as an InputStream
评论