Class#getResource與ClassLoader#getResource分析

  • Class#getResource方法的參數path能夠與以'/'開頭的絕對路徑或是不以'/'開頭的相對路徑,當以'/'開頭時,會從classpath路徑下獲取資源,當不以'/'開頭時,則從該類所在的包下獲取資源,xxx.class.getResource() 即xxx.class類所在包。
  • 而ClassLoader#getResource方法的參數卻不能以'/'開頭,其是從classpath下面獲取資源。

來看看下面代碼的結果java

package com.jdk.resource;
public class Resource {
	 public static void main(String[] args) throws Exception {
	        System.out.println(Resource.class.getResource(""));
	        System.out.println(Resource.class.getResource("/"));
	        System.out.println(Resource.class.getClassLoader().getResource(""));
	        System.out.println(Resource.class.getClassLoader().getResource("/"));
	    }
}

結果:api

file:/D:/workspace/JDKCore/bin/com/jdk/resource/
file:/D:/workspace/JDKCore/bin/
file:/D:/workspace/JDKCore/bin/
null

能夠看到Resource.class.getResource("")獲得的路徑classpath下Resource 類所在包,而Resource.class.getResource("/")爲classpath根路徑;而Resource.class.getClassLoader().getResource("")一樣爲classpath根路徑,Resource.class.getClassLoader().getResource("/")則爲空。 下面來看看Class#getResource方法的源碼this

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

從 cl.getResource(name)一行能夠看出,Class#getResource方法最終仍是調用了CalssLoader#getResource方法。再來看看name = resolveName(name)的實現,作了什麼處理,其調用了Class類的resolveName方法url

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

能夠看出,resolveName方法的目的是當參數name以'/'開頭就將'/'去除再返回,而當不以'/'開頭則根據這個類對應的帶包名全稱變換成具體的路徑名,如com.jdk.resource替換成com/jdk/resource。spa

如今應該明白最開始那個Resource 類中main方法的結果,實際上Class#getResource方法是去調CalssLoader#getResource的方法,只是在調用時會去判斷是從相對路徑仍是絕對路徑獲取資源。不過api是有一點點坑Class#getResource以'/'開頭與ClassLoader#getResource相同,再記住ClassLoader#getResource不能以'/'開頭就好了。.net

有了前面這個Classr#getResource與ClassLoader#getResource的對比,Class#getResourceAsStream與ClassLoader#getResourceAsStream其實也就ok了。ssr

Class#getResourceAsStream源碼:code

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源碼:接口

public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }

因此,能夠用下面三種方式獲得資源ip

  1. 利用Class#getResourceAsStream方法根據絕對路徑從classpath下面獲得,參數path以'/'開頭
  2. 利用Class#getResourceAsStream方法根據類所處包的相對路徑獲得,參數path不能以'/'開頭
  3. 利用ClassLoader#getResourceAsStream方法根絕對路徑從classpath下面獲得,參數path不能以'/'開頭

在com.jdk.resource目錄下建立一個test.properties文件,能夠用如下三種方式獲得

package com.jdk.resource;
public class Resource {
	 public static void main(String[] args) throws Exception {
		    //第一種方式
		    System.out.println(Resource.class.getResourceAsStream("test.properties"));
		    //第二種方式
		   System.out.println(Resource.class.getResourceAsStream("/com/jdk/resource/test.properties"));
		    //第三種方式
	System.out.println(Resource.class.getClassLoader().getResourceAsStream("com/jdk/resource/test.properties"));
	    }
}

結果:

java.io.BufferedInputStream@659e0bfd
java.io.BufferedInputStream@2a139a55
java.io.BufferedInputStream@15db9742

這對查看,Spring Resource 接口的實現ClassPathResource類getInputStream方法有必定幫助

public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}
相關文章
相關標籤/搜索