ClassLoader 和 Class加载资源的区别

    技术2026-01-06  9

    ClassLoader.getResource

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

    首先会去父类加载器找,直到父类为 null,就是 Bootstrap 加载器。如果整个过程都没有找到资源,则通过自己定义的 findResource 方法去找。对于 AppClassLoader,由于是 URLClassLoader 的子类,因此可以根据 URL 找到资源。

    注意:传入的 name 不能以 / 前导,否则找不到资源。使用时一般传入 com/cannedbread/app/xxx.properties 之类。

    Class.getResourceAsStream 方法 

    public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader0(); if (cl == null) { // A system class. return ClassLoader.getSystemResourceAsStream(name); } return cl.getResourceAsStream(name); }

    首先可以看到调用了 resolveName 方法,这个方法从源码来看,有三种处理:

    name 为 null

    方法返回 null

    name 没有以 / 前导

    这说明开发者意图传入一个相对路径。通过当前类获取 baseName,末尾再追加传入的 name。比如我传入 param.properties,当前类为 com.cannedbread.app,那么就会去 com/cannedbread/app/param.properties 下寻找。

    name 以 / 前导

    这说明开发者意图从根目录寻找。程序去除了 name 前面的 /,将 name 替换成相对路径,如果需要获取 com/cannedbread/app/param.properties,则必须传入 /com/cannedbread/app/param.properties。

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

    再回到 Class.getResourceAsStream,调用获取了 ClassLoader,如果为 null 则采用系统类加载器,否则采用获取的类加载器,一般多为 sun.misc.Launcher$AppClassLoader 

    public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader0(); if (cl == null) { // A system class. return ClassLoader.getSystemResourceAsStream(name); } return cl.getResourceAsStream(name); }

    下面再看看 ClassLoader.getResourceAsStream 方法,首先会查看父类加载器,一般 sun.misc.Launcher$AppClassLoader 的父类加载器是 sun.misc.Launcher$ExtClassLoader,如果没有资源就使用 Bootstrap 类加载器,所以会优先找父类加载器的资源。

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

    总结

    所有的资源都会转化为相对路径去获取,因此使用时需要注意。

    Processed: 0.047, SQL: 9