資源獲取解析

在 開發java程序的過程當中,咱們常常要作的一件事就是獲取資源。那麼什麼是資源呢?說白了,在計算機裏那就是一堆數據。只是這堆數據對咱們的java程 序有多種表現形式,通常來講有File,URL,InputStream等等。而單就文件這一項就有不少種:配置文件,java類文件,jps文件,圖 片、css、js文件等等。面對這林林總總的資源,咱們在設計一個讀取資源的接口時,就須要針對不一樣形式的資源提供方法,這樣就致使咱們的接口仍是與實際 的資源形式綁定在一塊兒,未能徹底的抽象。另外,在java程序中資源的存放位置也是各異的。有的存放在classpath中,有的存放在文件系統中,有的 存放在web應用中。而對於不一樣位置的資源,java程序獲取這些資源的方法各有不一樣。
 
A、獲取classpath中的資源:
Java代碼  URL url = this.getClass().getResource("resource_name");  
URL url = this.getClass().getClassLoader().getResource("resource_name");  
URL url = Thread.currentThread().getContextClassLoader().getResource("resource_name");  
 那麼在jdk中爲何又提供了三種方式來獲取classpath下的資源呢?這其中是有些來頭的。 
第一行代碼中是利用Class類的實例來獲取,第二行代碼是使用加載當前類的classloader來獲取。看下jdk中的源代碼會發現class類的實例最後仍是委託加載他的classloader來獲取資源的。
Java代碼  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);  
}  
 從上面的代碼中能夠看出,對於資源的加載並無像類加載所採用的雙親委託機制。而是當前類的classloader不爲null的情 況下先從當前類的 classloader中加載資源。而只有當前類的classloader爲null的時候才從system classloader中去加載資源。這樣能夠方便咱們自定義配置類覆蓋一些默認配置。固然,j2se應用中若是沒有特別定製classloader時, 咱們本身寫的類都是被system classloader加載的。到底利用class去獲取資源和利用classloader去獲取資源有什麼區別呢?區別就在 resolveName(name)這個方法中。兩種方式對於資源名稱的表示方式不一樣。下面是一個簡單的包結構,/表示類路徑的根 
/ 
|-com.cn.test 
   |-Test.class 
   |-test2.txt  
|-test1.txt
Java代碼  // 獲取與當前類在同一個包下的資源  
URL url1 = this.getClass().getResource("test2.txt");  
// 獲取com.cn.test包下的資源,需加/  
URL url2 = this.getClass().getResource("/com/cn/test/test2.txt");  
// 獲取類路徑根下的資源  
URL url3 = this.getClass().getClassLoader().getResource("test1.txt");  
// 獲取包com.cn.test包下的資源  
URL url4 = this.getClass().getResource("com/cn/test/test2.txt");  
 而第三利用當前線程的contextClassLoader來獲取資源的解釋能夠參考個人另外一篇

B、獲取文件系統中的資源
Java代碼  // 一、得到File對象  
File file = new File("test.txt");  
// 二、得到File對象的字節流  
InputStream in = new FileInputStream(file);  
 值得注意的是在File的構造函數File(String name) 中的name參數能夠是相對路徑和絕對路徑。相對路徑是相對於System.getProperties("user.dir")的。

C、獲取web應用中的資源
Java代碼  servletContext.getResourceAsStream(resource_name);  
 
resource_names爲相對於webroot的路徑表示。例如獲取web.xml,resource_name表示爲"/WEB-INF/web.xml" 

    面對上面介紹的各類資源表現形式和存放位置,難道java中就沒有提供一個統一處理方式嗎?有,java.net.URL。 
從名稱上來看 URL(Uniform Resource Locator) 統一資源定位器。看起來很好很強大。但不少時候使用它並不能定位到咱們須要的資源。 
    首先,它jdk中體統的URL能訪問的協議很是有限(固然能夠進行擴展,不過很麻煩);經常使用的有http,file,ftp等等。並無提供對classpath和servletContext中的資源的獲取方法。 
    另外,它沒有提供判斷資源是否存在的方法。每次只有等咱們真正去獲取資源的時候拋出異常才能知道資源沒法獲取。 
    其次,URL這個類的職責未劃分清楚,既用來表示資源有用來獲取其資源。
 
如下內容
From :  http://blog.csdn.net/ruyanhai/archive/2007/11/07/1871663.aspx
 
◆通常狀況下,咱們都使用相對路徑來獲取資源,這樣的靈活性比較大.
 
好比當前類爲com/bbebfe/Test.class
 
而圖像資源好比sample.gif應該放置在com/bbebfe/sample.gif
 
而若是這些圖像資源放置在icons目錄下,則應該是com/bbebfe/icons/sample.gif
 
經過當前類文件的路徑獲取資源主要有以下幾種方式:
 
· 假設當前類爲com.bbebfe.Test
 
· 包所在的文件夾爲bin
String imageName = "icons/sample.gif"
 
1, 經過Class.getResource()定位類路徑下的資源(bin/com/bbebfe/icons/sample.gif)
 
Class clazz = this.getClass();
 
URL url = clazz.getResource(imageName);
 
 
2,經過ClassLoader.getResource()定位包的根目錄下的資源(bin/icons/sample.gif)
 
Java代碼  Class clazz = this.getClass();  
  
URLClassLoader loader = (URLClassLoader)clazz.getClassLoader();  
  
URL url = loader.getResource(imageName);  
 3, 經過ClassLoader.findResource()提供本身定製的方式定位資源
Java代碼  URL url = loader.findResource(imageName);  
◆那麼這三種方法有那些區別, 咱們應該在什麼時候使用哪一種方法呢?
 
· Class.getResource() 方法
 
該方法實際經過該Class的Class Loader的getResource()方法來得到資源, 在調用ClassLoader的getResource()方法以前, Class.getResource()方法會對資源名稱作必定的處理,構建一個該資源的絕對名稱(absolute name, 大意是:
 
       +若是資源名稱以'/'('/u002f') 開始, 則資源的絕對名稱是'/'之後的部分.
 
              若是imageName是"/icons/sample.gif", 則在這裏會變成"icons/sample.gif"
 
       +不然對於其餘狀況, 絕對名稱將是以下形式(給資源名稱的前面加上modified_package_name/):
              modified_package_name/resource_name (修正的包名稱/資源名稱)
 
其中修正的包名稱含義是將當前對象所在的包名稱中的'.'('/u002e')替換爲'/'
 
若是ClassLoader.getResource()方法返回一個值爲null的URL, 則Class.getResource()方法最終會將資源請求交給ClassLoader.getSystemResource(java.lang.String).
 
· ClassLoader.getResource() 方法
 
該對資源進行查找, 資源的名稱是以'/'分隔的路徑, 這個方法首先查找本身的父親ClassLoader, 由本身的父ClassLoader來查找資源(實際上, 若是父親的父親不是空, 則父親仍會向上提交查找請求). 若是本身的父ClassLoader是null, 則查找Java虛擬機中內建的class loader, 並將資源請求提交給它們, 若是這些操做都失敗了, 則ClassLoader會調用本身的findResource()方法來查找資源.
 
· ClassLoader.findResource() 方法
 
該方法在內部查找指定的資源, 若是你實現了本身的Class Loader,則應該重載這個方法以本身特定的方式來查找類文件和資源.
 
 
 
◆經過以上的總結, 咱們能夠看到三點.
 
1, 不管是getResource(), 仍是findResource(), 這些方法都只是資源的定位方法, 最終都只是返回一個URL, 只是對資源的定位而已, 咱們隨後應經過本身的方法來讀取這些資源. 而在Class和ClassLoader中還定義的有getResourceAsStream方法, 該方法是getResource的加強版, 這裏就不介紹了.
 
 
2,若是須要以類爲相對路徑查找資源, 則應該調用Class.getResource()方法, 不要直接調用ClassLoader.getResource()方法. 另外, 除非是你本身定義了ClassLoader並重載了findResource方法,不然也不要直接調用ClassLoader.findResource方法, 由於在Class.getResource()方法中會對資源名稱做必定的處理, 這在上面介紹了, 下面舉個實例:
 
       假設個人當前類在Eclipse工程Database下, 類所在的包是com.bbebfe.test, 而icons目錄放在bin/com/bbebfe/test/目錄下, 我須要獲得icons/sample.gif文件的URL, 則調用this.getClass().getResource()獲得的URL是:
 
      file:/E:/MyLife/MyProjects/Eclipse3.2/Database/bin/com/bbebfe/test/icons/disremove.gif
 
 
 
3, 有時候咱們但願某個jar庫的圖像資源在同一個icons下統一管理, 而不是爲每一個包下面的Class建一個icons, 也就是說須要以庫爲相對路徑來查找資源, 此時則應該調用ClassLoader.getResource()方法, 舉個例子:
 
·某個工程有以下的包結構:
 
       com.bbebfe.ui
 
       com.bbebfe.test
 
       com.bbebfe.database
 
·若是以類爲相對路徑, 則在每一個包下都必須創建一個icons目錄, 並放置相應的資源文件. 以下:
 
       com.bbebfe.ui/icons/...
 
       com.bbebfe.test/icons/...
 
       com.bbebfe.database/icons/...
 
·而咱們可能但願在根目錄下放置一個icons目錄, 把全部資源放置在這裏管理, 這樣還能夠防止資源的重複. 就是以下形式
 
       com.bbebfe.ui
 
       com.bbebfe.test
 
       com.bbebfe.database
 
       icons/sample.gif ...
 
       則此時咱們應該調用ClassLoader.getResource方法, 因爲它沒有對資源名稱做處理, 也就是說沒有將修正的包名添加到資源名稱前, 因此它會在類所在的包的根下去查找資源.(運行java程序的語法是java com.bbebfe.ui.Test, 因此根目錄是com目錄的上級目錄).
 
 
 
◆最後, 在Java中對資源進行定位的方法有不少種, 在Eclipse源代碼中還有以下一段定位文件資源的代碼,尚未時間研究:
 
Java代碼  ProtectionDomain domain = Main.class.getProtectionDomain();  
  
CodeSource source = null;  
  
URL result = null;  
  
if (domain != null)  
  
source = domain.getCodeSource();//得到code source  
  
if (source != null)  
  
      result = source.getLocation();//得到URL  
  
            
  
String path = decode(result.getFile());//  
  
// normalize to not have leading / so we can check the form  
  
File file = new File(path);  
  
path = file.toString().replace('//', '/');  
  
// create a file URL (via File) to normalize the form (e.g., put  
  
// the leading / on if necessary)  
  
path = new File(path).toURL().getFile();
相關文章
相關標籤/搜索