Class.getResource()、ClassLoader.getResource()、ClassLoader.getSystemResource()区别

public java.net.URLgetResource(Stringname) {

       name = resolveName(name);

       ClassLoadercl =getClassLoader0();

       if (cl==null) {

           // A system class.

           return ClassLoader.getSystemResource(name);

       }

       returncl.getResource(name);

}

   private StringresolveName(Stringname) {

       if (name ==null) {

           returnname;

       }

       if (!name.startsWith("/")) {

           Class<?> c = this;

           while (c.isArray()) {

               c =c.getComponentType();

           }

           String baseName = c.getName();

           intindex =baseName.lastIndexOf('.');

           if (index != -1) {

               name =baseName.substring(0,index).replace('.','/')

                   +"/"+name;

           }

       } else {

           name =name.substring(1);

       }

       returnname;

    }

 

 

1)  Class.getResource(path)path如果以/开头,则查找classpath根下路径为path的资源,如果不以/开头,则resolveName,path加上当前类的包的路径(.替换为/),返回URL

Class.getResourceAsStream(path)同上,只不过返回的是InputStream

 

2)  ClassLoader.getResource(path)/下查找,path/开头会返回null

  ClassLoader.getResources(path)同上,返回多个

  ClassLoader.getResourceAsStream(path)/下查找,path/开头会返回null

 

3) ClassLoader.getSystemResource(path);

       ClassLoader.getSystemResourceAsStream(path);

   ClassLoader.getSystemResources(path);

这三个内部

publicstaticURLgetSystemResource(Stringname) {

       ClassLoader system = getSystemClassLoader();

       if (system ==null) {

           returngetBootstrapResource(name);

       }

       returnsystem.getResource(name);

    }

使用的SystemClassLoader规则跟2)一样。这个跟运行环境有关,tomcat下易返回null,慎用

另外一篇文章讲解很详细

http://www.cnblogs.com/drwong/p/5389631.html


文章内容

关于getSystemResource, getResource 的总结

项目中, 有时候要读取当前classpath下的一些配置文件. 之前用的读取配置文件的代码如下

复制代码
    public static Properties loadPropertiesFile(String fileName){
            Properties prop = new Properties();
        
            InputStream inStream = ClassLoader.getSystemResourceAsStream(fileName);
            if(null!=inStream){
                try {
                    prop.load(inStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return prop;
    }
复制代码

 

使用的方式是 ClassLoader.getSystemResourceAsStream(fileName)获取这个fileName对应的properties文件的输入流, 然后用prop对象的load方法. 用在生产环境的jstorm中一切正常, 但是切换到测试环境的jstorm后发现inStream总是null.

后来改使用如下方式, 在不同环境下都能正常使用了:

复制代码
    public static Properties loadPropertiesFile(String fileName){
        Properties prop = new Properties();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inStream = classLoader.getResourceAsStream(fileName);
        if(null!=inStream){
            try {
                prop.load(inStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return prop;
    }
复制代码

从当前线程获取到载入这个类的classloader, 然后再通过这个classloader来获取配置文件的InputStream. 到这里还是很不解,为啥会有这样的问题.于是从网上众多方法中选出几个来分析一下.

下面是测试代码,尝试读取程序跟目录的一个叫business.properties文件. 这个类是放在tomcat里运行的Restlet, 可以看成是一个简单restful接口. 除此之外我还加了个main方法, 用来不是在tomcat的环境里运行,而是直接运行常规Java程序那样运行的.

复制代码
@Path(value = "/test")
public class TestCode {
    public static void main(String[] args){
        TestCode tc = new TestCode();
        tc.testcode();

    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path(value = "getPath")
    public String testcode(){
     //方法1
        URL url1 = ClassLoader.getSystemResource("business.properties");
        System.out.println("url1:\t" + (url1 == null ? "null" : url1.getPath()));

        URL url1withSlash = ClassLoader.getSystemResource("/business.properties");
        System.out.println("url1/:\t" + (url1withSlash == null ? "null" : url1withSlash.getPath()));
     

        //方法2
        ClassLoader classLoader2 = this.getClass().getClassLoader();
        URL url2 = classLoader2.getResource("business.properties");
        System.out.println("url2:\t" + (url2 == null ? "null" : url2.getPath())+";classLoader is:"+classLoader2.toString());

        URL url2withSlash = classLoader2.getResource("/business.properties");
        System.out.println("url2/:\t" + (url2withSlash == null ? "null" : url2withSlash.getPath()));

        //方法3
        URL url3 = this.getClass().getResource("business.properties");
        System.out.println("url3:\t" + (url3 == null ? "null" : url3.getPath()));

        URL url3withSlash = this.getClass().getResource("/business.properties");
        System.out.println("url3/:\t" + (url3withSlash == null ? "null" : url3withSlash.getPath()));

        //方法4
        ClassLoader classLoader4 = Thread.currentThread().getContextClassLoader();
        URL url4 = classLoader4.getResource("business.properties");
        System.out.println("url4:\t" + (url4 == null ? "null" : url4.getPath())+";classLoader is:"+classLoader4.toString());

        URL url4withSlash = classLoader4.getResource("/business.properties");
        System.out.println("url4/:\t" + (url4withSlash == null ? "null" : url4withSlash.getPath()));
        return "OK";
    }
}
复制代码

 

下面看下通过tomcat运行和直接运行main方法输出内容的区别.

通过main方法,直接运行程序,输出结果是:

 

url1: /D:/thomas-dev/Team-Building-master/target/classes/business.properties
url1/: null
url2: /D:/thomas-dev/Team-Building-master/target/classes/business.properties;classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560
url2/: null
url3: null
url3/: /D:/thomas-dev/Team-Building-master/target/classes/business.properties
url4: /D:/thomas-dev/Team-Building-master/target/classes/business.properties;classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560
url4/: null

 

通过tomcat运行, 然后访问这个restful接口, 执行的结果是:

url1: null
url1/: null
url2: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties;classLoader is:WebappClassLoader
context: ROOT
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@1ae8873a

url2/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties
url3: null
url3/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties
url4: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties;classLoader is:WebappClassLoader
context: ROOT
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@1ae8873a

url4/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties

 

经过查询资料得知:

方法1:使用的是jvm的ClassLoader, 如果是直接运行的Java程序, 那么的确是调用jvm的ClassLoader, 于是调用的程序的根目录是可以获取这个文件的. 而在tomcat中,这个类并不是由系统自带的ClassLoader装载的, tomcat中而是由一个叫WebappClassLoader来装载的, jvm的ClassLoader取到的这文件是null

方法2: 获取加载当前类的ClassLoader, 这个ClassLoader会随着环境的变化而变化, 可以看到第一次直接运行的ClassLoader是jvm自带的classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560, 而在tomcat中是WebappClassLoader,它的父级是java.net.URLClassLoader@1ae8873a, 都能获取到properties文件, 所以方法2是可行的.

方法3:直接从当前对象的类调用getResource方法, 刚进去就调用了resolveName方法, 这个resolveName方法,判断文件路径是以/ 开头还是./开头, 来确定是相对与当前class文件目录还是相对于程序根目录, 然后也会像方法2那样获取加载当前类的ClassLoader, 如果获取不到则使用系统自带的ClassLoader.. 这个方法比较智能,可以使用,但是跟其他方法相比, 需要加一个 /表示程序根目录.

方法4: 获取加载当前线程的ClassLoader,当前类是在当前线程调用的,则他们的ClassLoader对象是一样的, 可以看他们输出的ClassLoader的内存地址是一样的. 

 

本次了解得也还是比较浅显,有一些深入问题没搞清楚, 以后深入了解ClassLoader之后再补充..


使用的SystemClassLoader规则跟2)。这个跟运行环境有关,tomcat下易返回null,慎用

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值