关于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:[email protected]
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:[email protected]
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:
[email protected]

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:
[email protected]

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:[email protected], 而在tomcat中是WebappClassLoader,它的父级是[email protected], 都能获取到properties文件, 所以方法2是可行的.

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

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

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

时间: 2024-11-04 14:34:21

关于getSystemResource, getResource 的总结的相关文章

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

Class.getResource(String path)path不以'/'开头时,默认是从此类所在的包下取资源:path以'/'开头时,则是从项目的ClassPath根下获取资源.在这里'/'表示ClassPathJDK设置这样的规则,是很好理解的,path不以'/'开头时,我们就能获取与当前类所在的路径相同的资源文件,而以'/'开头时可以获取ClassPath根下任意路径的资源.如下所示的例子: 1 2 3 4 5 6 7 8 public class Test { public stat

Class.getResource和ClassLoader.getResource区别与分析

零. 前言 在一个获取文件路径的代码内容上发现单元测试出现NullPointer, 但是这段代码却在服务器正常运行了两年多, 借此机会想认真探索下这两个方法的区别. 一. 区别 区别从代码输出结果来看比较清晰, 文件结构图: 以下为在多个场景下使用方式的代码: package com.wenniuwuren.test; /** * 获取文件路径 * Created by wenniuwuren on 15/8/14. */ public class GetResourceTest { publi

Java Class.getResource和ClassLoader.getResource的使用

Java中获取资源文件的时候,经常会用到Class.getResource和ClassLoader.getResource,本文给大家说一下这两者方法在获取资源文件时的路径差异. Class.getResource(String path) path不以'/'开头时,默认是从此类所在的包下取资源: path以'/'开头时,则是从项目的ClassPath根下获取资源.在这里'/'表示ClassPath JDK设置这样的规则,是很好理解的,path不以'/'开头时,我们就能获取与当前类所在的路径相同

java: 关于从jar中读取资源遇到的问题getClass().getResource(...)

在Java的程序发布中,很多人会选择采用二进制的jar的格式进行发布,怎么样读取Jar里面的资源呢?主要是采用ClassLoader的下面几个方法来实现:public URL getResource(String name):public InputStream getResourceAsStream(String name) public static InputStream getSystemResourceAsStream(String name)public static URL get

myBatis中的注解@Param、返回值为Map、JAVA读取Excel并解析文本、Class.getResource()和ClassLoader.getResource()

myBatis中的注解@Param:http://blog.csdn.net/gao36951/article/details/44258217:  http://www.cnblogs.com/thomas12112406/p/6217211.html. myBatis返回值为Map:http://blog.csdn.net/werewr342352321df/article/details/11892755. ====================== JAVA读取Excel并解析文本:h

[Java开发之路](18)关于Class.getResource和ClassLoader.getResource的路径问题

Java中取资源时,经常用到Class.getResource和ClassLoader.getResource.昨天老师讲解题目时候,问我们为什么你们都是在文件前家上"/": String path = Resources.class.getResource("/a.txt").getPath(); 注:在Resources文件下创建了a.txt文件 我想我反正是试出来的,不使用"/"不行.为了正式解答心中的疑惑,我们正式来看看Resources

Class和ClassLoader.getResource获取资源

资源就是系统内.项目内的各种文件.在Java中获取可以用File类和Class.getResource获取资源,直观的说,File是依赖于文件系统和操作系统的,通过相对和绝对路径定位,使用File接口可以用于获取系统内任何路径下的普通文件.Class和ClassLoader可以用于获取项目环境中的资源,'环境中'的具体定义就是classpath.classpath相当于getResource参数的跟目录,ClassLoader.getResource是只能从这个根目录开始定位资源,Class.g

classpath获取--getResource()

在java中的API里,有两种方式来使用classpath读取资源. 1. Class的getResource() 2. ClassLoader的getResource() 但是两者有一定区别,运行以下程序: package zero.xml.config; public class Main { public static void main(String[] args) { new Main().testGetResource(); } public void testGetResource

java 引用资源-ClassLoader.getResource()方法

如图,eclipse中我的包结构为:,我在 spt.app.MainFrame 中可以通过一下代码段使用资源: public static Object obj = ImageIconProxy.class.getClassLoader().getResource("img/absent.png"); 但是我们不能在A类中,引用别的类声明类似上面的方法,例如,我们再 spt.proxy.ImageIconProxy 类中声明: public static Object obj = Im