Web基础之Class与ClassLoader的getResource区别

前言

在Web开发中经常要加载项目下的各种资源,有一种方法是用ClassLoader或者Class类提供的getResource来加载。本文将从代码上来分析两者的区别

正文

经常看到下面类似的代码,往往很容易搞混

public static void loadSourceWithClassLoader() {
        SysFilePath.class.getResource("/org/easyutil/resources/utf8.txt"); //从classpath下加载资源
        SysFilePath.class.getResource("utf8.txt");//从当前目录下加载资源
        SysFilePath.class.getClassLoader().getResource("org/easyutil/resources/utf8.txt");//从classpath下加载资源
    }
  • important note:这里需要记住一点的就是class.getResource使用ClassLoader类中的getResource实现的。也就是说两者的本质都是使用加载器来加载资源

加载器种类很多,在java项目有下面三种加载器 
1)Bootstrap ClassLoader 
2)Extension ClassLoader 
3)App ClassLoader 
如果是Web的项目且使用的是Tomcat容器,那么加载器还包括 
4)WebAppClassLoader 
不同的加载器都有一个指定的加载目录,自己理解为是classpath.所以ClassLoader.getResource(path)从classpath中加载path路径下的资源,这里path是开头不能是“/”且其后按照/分割的格式

从上面的可以知道了 
SysFilePath.class.getClassLoader().getResource("org/easyutil/resources/utf8.txt"); 
加载的就是classpath路径下的资源,具体实现代码如下

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

从加载器继承体系的最上层查找资源,如果找不到资源在一层层下来直到使用当前加载器(默认加载目录)加载资源(findResource)

再看看Class类中的getResource怎么加载资源的.其实开头是否要加“/”是业务要求导致的(这个类的功能需求决定的),而本质是使用ClassLoader来加载资源,它要求路径开头不需要“/”.这么说来开头是否要“/”会产生不同的代码逻辑处理,我们看看这些不同逻辑是怎么处理的

Class类的getResource

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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最后获取当前的ClassLoader加载资源,那么这里的name是什么样子呢?

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) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {
            name = name.substring(1);
        }
        return name;
    }

如果开头不是“/”则获取当前类的包路径,“.”转成以“/”分割的路径然后连接上name(外面指定的资源路径),这样直接定位到classpath下当前所在的路径下的指定name的路径中的资源 
如果以“/”开头则,直接去掉“/”,表示定位到classpath下的name路径所在的资源

最后总结得出下面结论 
class.getResource有下面两个算法

  1. 如果以“/”开头则加载classpath下的资源
  2. 如果不以“/”开头则加载当前目录下的资源

    ClassLoader.getResource算法

  3. 从classpath加载资源,且开头不能是“/”其后按照“/”分割的路径(相对路径)

猜你喜欢

转载自blog.csdn.net/it_manman/article/details/79792368