深入探討、理解Java的CLASSPATH
從表面上看,的(類路徑)很簡單,但一直以來它都是一個產生問題和混亂的根源。本文介紹classpath的基本知識、可能產生的問題,並提供了一個簡單的classpath管理工具。
和Java類路徑(classpath)打交道的過程中,者偶爾會遇到麻煩。這是因為,類裝載器實際裝入的是哪一個類有時並不顯而易見,當應用程式的classpath包含大量的類和目錄時,情況尤其嚴重。本文將提供一個工具,它能夠顯示出被裝入類檔案的絕對路徑名。
一、Classpath基礎
Java虛擬機器()藉助類裝載器裝入應用程式使用的類,具體裝入哪些類根據當時的需要決定。CLASSPATH環境變數告訴類裝載器到哪裡去尋找第三方提供的類和定義的類。另外,你也可以使用JVM命令列引數-classpath分別為應用程式指定類路徑,在-classpath中指定的類路徑覆蓋CLASSPATH環境變數中指定的值。
類路徑中的內容可以是:檔案的目錄(包含不在包裡面的類),包的根目錄(包含已打包的類),包含類的檔案檔案(比如.zip檔案或者.jar檔案)。在Unix家族的系統上,類路徑的各個專案由冒號分隔,在MS Windows系統上,它們由分號分隔。
類裝載器以委託層次的形式組織,每一個類裝載器有一個父類裝載器。當一個類裝載器被要求裝載某個類時,它在嘗試自己尋找類之前會把請求先委託給它的父類裝載器。系統類裝載器,即由安裝在系統上的或JRE提供的預設類裝載器,透過CLASSPATH環境變數或者-classpath這個JVM命令列引數裝入第三方提供的類或者使用者定義的類。系統類裝載器委託擴充套件類裝載器裝入使用Java Extension機制的類。擴充套件類裝載器委託自舉類裝載器(bootstrap class loader)裝入核心JDK類。
你可以自己開發特殊的類裝載器,定製JVM如何動態地裝入類。例如,大多數Servlet引擎使用定製的類裝載器,動態地裝入那些在classpath指定的目錄內發生變化的類。
必須特別注意的是(也是令人吃驚的是),類裝載器裝入類的次序就是類在classpath中出現的次序。類裝載器從classpath的第一項開始,依次檢查每一個設定的目錄和壓縮檔案,嘗試找出待裝入的類檔案。當類裝載器第一次找到具有指定名字的類時,它就把該類裝入,classpath中所有餘下的專案都被忽略。
看起來很簡單,對吧?
二、可能出現的問題
不管他們是否願意承認,初學者和富有經驗的Java開發者都一樣,他們都曾經在某些時候(通常是在那些最糟糕的情形下)被冗長、複雜的classpath欺騙。應用程式所依賴的第三方類和使用者定義類的數量逐漸增長,classpath也逐漸成了一個堆積所有可能的目錄和檔案檔名的地方。此時,類裝載器首先裝載的究竟是哪一個類也就不再顯而易見。如果classpath中包含重複的類入口,這個問題尤其突出。前面已經提到,類裝載器總是裝載第一個它在classpath中找到的具有合適名字的類,從實際效果看,它“隱藏”了具有合適名字但在classpath中優先順序較低的類。
如果不小心,你很容易掉進這個classpath的陷阱。當你結束了一天漫長的工作,最後為了讓應用程式使用最好、最新的類,你把一個目錄加入到了classpath,但與此同時,你卻忘記了:在classpath的另一個具有更高優先順序的目錄下,存放著該類的另一個版本!
三、一個簡單的classpath工具
優先順序問題是扁平路徑宣告方法與生俱來固有的問題,但它不是隻有Java的classpath才有的問題。要解決這個問題,你只需站到富有傳奇色彩的軟體巨構的肩膀上:Unix作業系統有一個which命令,在命令引數中指定一個名字,which就會顯示出當這個名字作為命令執行時執行檔案的路徑名。實際上,which命令是分析PATH變數,然後找出命令第一次出現的位置。對於Java的類路徑管理來說,這應該也是一個好工具。在它的啟發之下,我著手設計了一個Java工具JWhich。這個工具要求指定一個Java類的名字,然後根據classpath的指引,找出類裝載器即將裝載的類所在位置的絕對路徑。
下面是一個JWhich的使用例項。它顯示出當Java類裝載器裝載com.clarkware.ejb.ShoppingCartBean類時,該類第一次出現位置的絕對路徑名,查詢結果顯示該類在某個目錄下:
> java JWhich com.clarkware.ejb.ShoppingCartBean
Class 'com.clarkware.ejb.ShoppingCartBean' found in '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class'
下面是第二個JWhich的使用例項。它顯示出當Java類裝載器裝載javax.servlet.http.HttpServlet類時,該類第一次出現位置的絕對路徑名,查詢結果顯示該類在某個檔案檔案中:
> java JWhich javax.servlet.http.HttpServlet
Class 'javax.servlet.http.HttpServlet' found in 'file:/home/mclark/lib/servlet.jar!/javax/servlet/http/HttpServlet.class'
四、JWhich的工作過程
要精確地測定classpath中哪一個類先被裝載,你必須深入到類裝載器的思考方法。事實上,具體實現的時候並沒有聽起來這麼複雜——你只需直接詢問類裝載器就可以了!
1: public class JWhich {
2:
3: /**
4: * 根據當前的classpath設定,
5: * 顯示出包含指定類的類檔案所在
6: * 位置的絕對路徑
7: *
8: * @param className
9: */
10: public static void which(String className) {
11:
12: if (!className.startsWith("/")) {
13: className = "/" + className;
14: }
15: className = className.replace('.', '/');
16: className = className + ".class";
17:
18: java.net.URL classUrl =
19: new JWhich().getClass().getResource(className);
20:
21: if (classUrl != null) {
22: System.out.println("nClass '" + className +
23: "' found in n'" + classUrl.getFile() + "'");
24: } else {
25: System.out.println("nClass '" + className +
26: "' not found in n'" +
27: System.getProperty("java.class.path") + "'");
28: }
29: }
30:
31: public static void main(String args[]) {
32: if (args.length > 0) {
33: JWhich.which(args[0]);
34: } else {
35: System.err.println("Usage: java JWhich
36: }
37: }
38: }
首先,你必須稍微調整一下類的名字以便類裝載器能夠接受(12-16行)。在類的名字前面加上一個“/”表示要求類裝載器對classpath中的類名字進行逐字精確匹配,而不是嘗試隱含地加上呼叫類的包名字字首。把所有“.”轉換為“/”的目的是,按照類裝載器的要求,把類名字格式化成一個合法的URL資源名。
接下來,程式向類裝載器查詢資源,這個資源的名字必須和經過適當格式化的類名字匹配(18-19行)。每一個Class物件維護著一個對裝載它的ClassLoader物件的引用,所以這裡是向裝載JWhich類的類裝載器查詢。Class.getResource()方法實際上委託裝入該類的類裝載器,返回一個用於讀取類檔案資源的URL;或者,當指定的類名字不能在當前的classpath中找到時,Class.getResource()方法返回null。
最後,如果當前的classpath中能夠找到指定的類,則程式顯示包含該類的類檔案所在位置的絕對路徑名(21-24行)。作為一種除錯輔助手段,如果當前classpath中不能找到指定的類,則程式獲取java.class.path系統屬性並顯示當前的classpath(24-28行)。
很容易想象,在使用Servlet引擎classpath的Java Servlet中,或者在使用伺服器classpath的EJB元件中,上面這段簡單的程式碼是如何運作。例如,如果JWhich類是由Servlet引擎的定製類裝載器裝入,那麼程式將用Servlet引擎的類裝載器去尋找指定的類。如果Servlet引擎的類裝載器不能找到類檔案,它將委託它的父類裝載器。一般地,當JWhich被某個類裝載器裝入時,它能夠找出當前類裝載器以及所有其父類裝載器所裝入的所有類。
如果需要是所有發明之母,那麼幫助我們管理Java類路徑的工具可以說遲到了很長時間。Java新聞組和郵件列表中充塞著許多有關classpath的問題,現在JWhich為我們提供了一個簡單卻強大的工具,幫助我們在任何環境中徹底玩轉Java類路徑。
[@more@]來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/21107256/viewspace-1018786/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java執行緒的深入探討Java執行緒
- Java HashMap工作原理深入探討JavaHashMap
- Java執行緒的深入探討 (轉)Java執行緒
- 深入探討 Java 類載入器Java
- 深入探討 UndefinedUndefined
- IsPostBack深入探討
- Java原始碼分析:深入探討Iterator模式Java原始碼模式
- Oracle Stream 深入探討Oracle
- Java Eclipse ClassPath如何理解JavaEclipse
- 深入探討單例模式單例模式
- 深入探討 Java Spring 框架事務註釋JavaSpring框架
- 深入探討Java中的異常與錯誤處理Java
- 深入探討ROP 載荷分析
- 深入探討JavaScript函式物件JavaScript函式物件
- 深入探討 Room 2.4.0 的最新進展OOM
- Sql Server深入的探討鎖機制SQLServer
- 深入探討前端元件化開發前端元件化
- 深入探討程式間通訊的重要性:理解不同的通訊機制(下)
- 深入探討程式間通訊的重要性:理解不同的通訊機制(上)
- IsPostBack深入探討(轉轉轉轉轉)
- CSS中Position、Float屬性深入探討CSS
- 深入探討!Batch 大小對訓練的影響BAT
- SQL Server之旅(14):深入的探討鎖機制SQLServer
- 深入探討Spring Boot中的引數傳遞Spring Boot
- 深入探討 CSS 特性檢測 @supports 與 ModernizrCSS
- 深入探討下SSR與CSR有啥不同
- Java classpath的獲取Java
- Java類、物件以及(靜態)方法的探討Java物件
- ORACLE專題深入探討精華帖連結收集Oracle
- Java開發者的神經網路進階指南:深入探討交叉熵損失函式Java神經網路熵函式
- 【話題討論】深入探討儲存如何劃分、使用_storage planning
- 我的理解——關於“ERP過時論”的探討(轉)
- java的classpath萬用字元Java字元
- Sql Server之旅——第十四站 深入的探討鎖機制SQLServer
- 深入探討微服務架構中的同步通訊機制微服務架構
- 探討Java中的多執行緒概念 - foojayJava執行緒
- opencv實戰——影像矯正演算法深入探討OpenCV演算法
- 專訪:深入探討SQL Server主資料服務ATSQLServer