所謂熱部署,就是在應用正在運行的時候升級軟件,卻不需要重新啟動應用。對于Java應用程序來說,熱部署就是在運行時更新Java類文件。在基于Java的應用服務器實現熱部署的過程中,類裝入器扮演著重要的角色。大多數基于Java的應用服務器,包括EJB服務器和Servlet容器,都支持熱部署。類裝入器不能重新裝入一個已經裝入的類,但只要使用一個新的類裝入器實例,就可以將類再次裝入一個正在運行的應用程序。
由于類裝入器擔負著把代碼裝入JVM的重任,所以它的體系結構應當保證整個平臺的安全性不會受到威脅。每一個類裝入器要為它裝入的類定義一個獨立的名稱空間,因此運行時,一個類由它的包名稱和裝入它的類裝入器兩者結合唯一地標識。
在名稱空間之外,類不可見,運行時不同名稱空間的類之間有一種保護性的“隔離墻”,在Java 2向父類委托的類裝入模式中,類裝入器可以請求其父類裝入器裝入的類,因此類裝入器需要的類不一定全部由它自己裝入。
在Java運行環境中,不同的類裝入器從不同的代碼庫裝入類,之所以要將各個類裝入器代碼庫的位置分開,是為了便于給不同的代碼庫設定不同的信任級別。在JVM中,由bootstrap類裝入器裝入的類具有最高的信任級別,用戶自定義類裝入器代碼庫的信任級別最低。此外,類裝入器可以把每一個裝入的類放入一個保護域,保護域定義了代碼執行時所擁有的權限。
如果要以系統安全策略(一個java.security.Policy的實例)為基礎定義代碼的權限,定制類裝入器必須擴展java.security.SecureClassLoad類,調用其defineClass方法,SecureClassLoad類的defineClass方法有一個java.security.CodeSource參數。defindClass方法從系統策略獲得與CodeSource關聯的權限,以此為基礎定義一個java.security.ProtectionDomain。詳細介紹該安全模型已經超出了本文的范圍,請讀者自行參閱有關JVM內部機制的資料。
類裝入器裝入的最小執行單元是Java .class文件。Java .class文件包含Java類的二進制描述,其中有可執行的字節碼以及該類用到的對其他類的引用,包括對Java標準API里面的類的引用。簡單地說,類裝入器首先找到要裝入的Java類的字節碼,讀入字節碼,創建一個java.lang.Class類的實例。做好這些準備之后,類就可以被JVM執行了。
當JVM最初開始運行時,它里面不裝入任何類。如果要求JVM執行一個程序,被執行的類首先裝入,字節碼執行期間會引用到其他類和接口,這些被引用到的類和接口隨之也被裝入。因此,有人把JVM的類裝入方式稱為“懶惰的”裝入方式,即只有必須用到某個類時才會裝入它(而不是預先裝入各種可能用到的類),正因為如此,開始時JVM不必知道運行時要裝入哪些類。在Java平臺上,懶惰的裝入方式是實現動態可擴展性機制的關鍵因素之一。在本文的后面,你將會看到通過實現一個定制的Java類裝入器,我們可以為Java運行時環境加入許多有趣的功能
一、委托模式
Java 2運行時環境中有多個類裝入器的實例,每一個類裝入器的實例從不同的代碼庫裝入Java類。例如,Java核心API類由bootstrap(或primordial)類裝入器裝入,應用程序的類由system(或application)類裝入器裝入。另外,應用程序可以自定義類裝入器從指定的代碼庫裝入類。Java 2定義了類裝入器之間的父-子關系,每一個類裝入器(bootstrap除外)都有一個父類裝入器,形成一個由類裝入器構成的樹形結構,bootstrap類裝入器是這個樹形結構的根,因此bootstrap沒有父類裝入器。
當客戶程序請求類裝入器裝入某個類時,類裝入器按照下面的算法裝入一個類:
⑴ 首先執行一次檢查,查看客戶程序請求的類是否已經由當前的類裝入器裝入。如果是,則返回已裝入的類,請求處理完畢。JVM緩沖了類裝入器裝入的所有類,已經裝入的類不會被再次裝入。
⑵ 如果尚未裝入類,則裝入類的請求被委托給父類裝入器,這個操作發生在當前的類裝入器嘗試裝入指定的類之前。委托裝入類的操作一直向上傳遞,直至bootstrap類裝入器,如前所述,bootstrap類裝入器是處于樹形結構的頂層,它不再有可委托的父類裝入器。
⑶ 如果父類裝入器未能裝入指定的類,則當前的類裝入器將嘗試搜索該類。每一個類裝入器有預定義的搜索和裝入類的位置。例如,bootstrap類裝入器搜索sun.boot.class.path系統屬性中指定的位置(包括目錄和zip、jar文件),system類裝入器搜索的位置由JVM開始運行時傳入的CLASSPATH命令行變量指定(相當于設置java.class.path系統屬性)。如果找到了類,則類裝入器將類返回給系統,請求處理完畢。
⑷ 如果找不到類,則拋出java.lang.ClassNotFoundException異常。