Java 中如何获取 classpath 下资源文件?

Java 中的资源有多种,如File,URL, InputStream等,本篇将介绍在Java中获取classpath下的资源文件的几种方式。

一 Class.getResource

String name1 = "/prop/system.properties";
URL url1 = App.class.getResource(name1);

二 Class.getResourceAsStream

String name1 = "/prop/system.properties";
InputStream inputStream1 = App.class.getResourceAsStream(name1);

三 ClassLoader.getResource

String name2 = "prop/system.properties";
 URL url2 = App.class.getClassLoader().getResource(name2);

四 ClassLoader.getResourceAsStream

String name2 = "prop/system.properties";
InputStream inputStream2 = App.class.getClassLoader().getResourceAsStream(name2);

五 ResourceBundle.getBundle

String name3 = "prop/system";
ResourceBundle resourceBundle = ResourceBundle.getBundle(name3);

注意资源名称不包含.properties

Class、ClassLoader获取资源文件区别

  • Class.getResource和Class.getResourceAsStream中,资源名称以"/"开头表示绝对定位,为类路径根目录下的资源文件, 否则资源名称被解析为以Class类的包名为父目录
  • ClassLoader.getResource和ClassLoader.getResourceAsStream中,资源名称不能以"/"开头,否则将找不到文件。

源码分析

Class类部分源码如下:

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {
    
    

    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);
    }
    
    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);
    }
    
    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;
    }
    
    ClassLoader getClassLoader0() {
    
     return classLoader; }

}
  • Class的getResource和getResourceAsStream首先调用resolveName方法解析资源名称,然后调用ClassLoader的getResource或getResourceAsStream方法;如果Class类中的域classLoader
    为null还将调用ClassLoader.getSystemResource或ClassLoader.getSystemResourceAsStream方法获取系统资源。
  • Class的resolveName方法解析资源名称时对方法参数进行处理,如果以"/“开头则把开头的”/"截取,否则取包名为父目录和方法参数进行拼接。

ClassLoader部分源码如下:

public abstract class ClassLoader {
    
    

    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;
    }
    
    public InputStream getResourceAsStream(String name) {
    
    
        URL url = getResource(name);
        try {
    
    
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
    
    
            return null;
        }
    }
}

  • ClassLoader的getResourceAsStream方法调用了getResource方法
  • ClassLoader的getResource方法首先尝试使用父类加载器获取资源,未获取到则调用findResource方法查找资源,findResource方法由子类覆盖。

总结

  • Class.getResource、 Class.getResourceAsStream、ClassLoader.getResourceAsStream最终都调用ClassLoader.getResource
  • 如果将properties文件转换为java类Properties,可以使用ResourceBundle.getBundle方法。

猜你喜欢

转载自blog.csdn.net/zzuhkp/article/details/106445167