什么是ClassLoader
ClassLoader是一個抽象類,我們用它的實例對象來裝載類,它負責將 Java 字節碼裝載到
JVM 中,
并使其成為
JVM 一部分。
JVM 的類動態裝載技術能夠在運行時刻動態地加載或者替換系統的某些功能模塊,而不影響系統其他功能模塊的正常運行。一般是通過類名讀入一個class文件來裝載這個類,(其它加載形式暫時沒有研究過)。
ClassLoader裝載過程
類裝載就是尋找一個類或是一個接口的字節碼文件并通過解析該字節碼來構造代表這個類或是這個接口的
class 對象的過程。在
Java 中,類裝載器把一個類裝入
Java 虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三步,除了解析外,其它步驟是嚴格按照順序完成的,各個步驟的主要工作如下:
1.
裝載:查找和導入類或接口的字節碼;
2.
鏈接:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;
l
校驗:檢查導入類或接口的二進制數據的正確性;
l
準備:給類的靜態變量分配并初始化存儲空間;
l
解析:將符號引用轉成直接引用;
3.
初始化:激活類的靜態變量的初始化
Java 代碼和靜態
Java 代碼塊。
裝載的實現
JVM 中類的裝載是由
ClassLoader 和它的子類來實現的。
Java ClassLoader 是一個重要的
Java 運行時系統組件,它負責在運行時查找和裝入
Java 字節碼。
在
Java 中,
ClassLoader 是一個抽象類,它在包
java.lang 中。可以這樣說,只要了解了
ClassLoader 中的一些重要的方法,再結合上面所介紹的
JVM 中類裝載的具體的過程,對動態裝載類這項技術就有了一個比較大概的掌握,這些重要的方法包括以下幾個:
1.
loadCass 方法:
loadClass(String name ,boolean resolve) 其中
name 參數指定了
JVM 需要的類的名稱
, 該名稱以類的全限定名表示,如
Java.lang.Object ;
resolve 參數告訴方法是否需要解析類,在初始化類之前,應考慮類解析,并不是所有的類都需要解析,如果
JVM 只需要知道該類是否存在或找出該類的超類,那么就不需要解析。這個方法是
ClassLoader 的入口點。
2.
defineClass 方法
這個方法接受類文件的字節數組并把它轉換成
Class 對象。字節數組可以是從本地文件系統或網絡裝入的數據。它把字節碼分析成運行時數據結構、校驗有效性等等。
3.
findSystemClass 方法
findSystemClass
方法從本地文件系統裝入
Java 字節碼。它在本地文件系統中尋找類文件,如果存在,就使用
defineClass 將字節數組轉換成
Class 對象。當運行
Java 應用程序時
, 這是
JVM 正常裝入類的缺省機制。
4.
resolveClass 方法
resolveClass(Class c) 方法解析裝入的類,如果該類已經被解析過那么將不做處理。當調用
loadClass 方法時
, 通過它的
resolve 參數決定是否要進行解析。
5.
findLoadedClass 方法
當調用
loadClass 方法裝入類時
, 調用
findLoadedClass 方法來查看
ClassLoader 是否已裝入這個類
, 如果已裝入
, 那么返回
Class 對象
, 否則返回
NULL 。如果強行裝載已存在的類
, 將會拋出鏈接錯誤。
java.lang.Class類
某個類的所有實例內部都有一個引用,指向該類對應的Class的實例的位置,每個java類對應的Class實例可以當作是類在內存中的代理人。所以當要獲得類的信息(如有哪些類變量,有哪些方法)時,都可以讓類對應的Class的實例代勞.java的Reflection機制就大量的使用這種方法來實現。但是Class類無法手工實例化,當載入任意類的時候自動創建一個該類對應的Class的實例。每個java類都是由某個classLoader(ClassLoader的實例)來載入的,因此Class類別的實例中都會他的ClassLoader的實例的引用。可以通過getClass.getClassLoader()得到CLassLoader的實例。
java動態載入class的兩種方式:
1)implicit隱式,即利用實例化才載入的特性來動態載入class
2)explicit顯式方式,又分兩種方式:
a)java.lang.Class的forName()方法
b)java.lang.ClassLoader的loadClass()方法
各種java類由哪些classLoader加載?
1)java類可以通過實例.getClass.getClassLoader()得到
2)接口由AppClassLoader(SystemClassLoader)載入
,SystemClassLoader:可以由ClassLoader.getSystemClassLoader()獲得實例
3)ClassLoader類由bootstrap
loader載入
ClassLoader hierachy:
1)jvm初始化產生bootstrap loader。并設定它的父ClassLoader為null
2)bootstrap loader建立AppClassLoader,載入 運行java.exe時
的-cp或-classpath中的類(每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類系統默認的contextClassLoader是systemClassLoader)
Java 類裝載的代理結構
根
(Bootstrap) 裝載器:該裝載器沒有父裝載器,它是
JVM 實現的一部分,從
sun.boot.class.path 裝載運行時庫的核心代碼。
擴展
(Extension) 裝載器:繼承的父裝載器為根裝載器,不像根裝載器可能與運行時的操作系統有關,這個類裝載器是用純
Java 代碼實現的,它從
java.ext.dirs ( 擴展目錄
) 中裝載代碼。
系統
(System or Application) 裝載器:裝載器為擴展裝載器,我們都知道在安裝
JDK 的時候要設置環境變量
(CLASSPATH ) ,這個類裝載器就是從
java.class.path(CLASSPATH 環境變量
) 中裝載代碼的,它也是用純
Java 代碼實現的,同時還是用戶自定義類裝載器的缺省父裝載器。
小應用程序
(Applet) 裝載器:父裝載器為系統裝載器,它從用戶指定的網絡上的特定目錄裝載小應用程序代碼。
java中的代理結構是自上而下查找類的,這與很多web裝載器不同。
tomcat中的實現的子ClassLoader的結構
Tomcat Server在啟動的時候將構造一個ClassLoader樹,以保證模塊的類庫是私有的
Tomcat
Server的ClassLoader結構如下:
---------------------------+
| Bootstrap |
| | |
| System
|
| | |
|
Common |
| / \ |
| Catalina Shared |
| / \
|
| WebApp1 WebApp2 |
---------------------------+
其中:
- Bootstrap -
載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
- System - 載入$CLASSPATH/*.class
-
Common - 載入$CATALINA_HOME/common/...,它們對TOMCAT和所有的WEB APP都可見
- Catalina -
載入$CATALINA_HOME/server/...,它們僅對TOMCAT可見,對所有的WEB APP都不可見
- Shared -
載入$CATALINA_HOME/shared/...,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見)
- WebApp? -
載入ContextBase?/WEB-INF/...,它們僅對該WEB APP可見
ClassLoader被組織成樹形,一般的工作原理是:
1) 線程需要用到某個類,于是contextClassLoader被請求來載入該類
2) contextClassLoader請求它的父ClassLoader來完成該載入請求
3)
如果父ClassLoader無法載入類,則contextClassLoader試圖自己來載入
注意:WebApp?ClassLoader的工作原理和上述有少許不同:
它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無法載入,再請求父ClassLoader完成