• 关于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: " + (url1 == null ? "null" : url1.getPath())); URL url1withSlash = ClassLoader.getSystemResource("/business.properties"); System.out.println("url1/: " + (url1withSlash == null ? "null" : url1withSlash.getPath()));     

    //方法2 ClassLoader classLoader2
    = this.getClass().getClassLoader(); URL url2 = classLoader2.getResource("business.properties"); System.out.println("url2: " + (url2 == null ? "null" : url2.getPath())+";classLoader is:"+classLoader2.toString()); URL url2withSlash = classLoader2.getResource("/business.properties"); System.out.println("url2/: " + (url2withSlash == null ? "null" : url2withSlash.getPath()));
    //方法3 URL url3
    = this.getClass().getResource("business.properties"); System.out.println("url3: " + (url3 == null ? "null" : url3.getPath())); URL url3withSlash = this.getClass().getResource("/business.properties"); System.out.println("url3/: " + (url3withSlash == null ? "null" : url3withSlash.getPath()));
    //方法4 ClassLoader classLoader4
    = Thread.currentThread().getContextClassLoader(); URL url4 = classLoader4.getResource("business.properties"); System.out.println("url4: " + (url4 == null ? "null" : url4.getPath())+";classLoader is:"+classLoader4.toString()); URL url4withSlash = classLoader4.getResource("/business.properties"); System.out.println("url4/: " + (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之后再补充..

  • 相关阅读:
    [BZOJ5338][TJOI2018]xor(可持久化Trie)
    [BZOJ4592][SHOI2015]脑洞治疗仪(线段树)
    [BZOJ4571][SCOI2016]美味(贪心+主席树)
    [BZOJ4570][SCOI2016]妖怪(凸包)
    [BZOJ4569][SCOI2016]萌萌哒(倍增+并查集)
    [BZOJ4567][SCOI2016]背单词(Trie+贪心)
    [BZOJ4565][HAOI2016]字符合并(区间状压DP)
    [BZOJ4561][JLOI2016]圆的异或并(扫描线)
    [BZOJ2650]积木
    [清橙A1210]光棱坦克
  • 原文地址:https://www.cnblogs.com/drwong/p/5389631.html
Copyright © 2020-2023  润新知