Java 動態載入

來源:互聯網
上載者:User
第一部分:Java虛擬機器啟動時,關於類載入方面的一些動作當使用java ProgramName.class運行程式時,Java找到JRE,接著找到jvm.dll,把該動態庫載入記憶體,這就是JVM。然後載入其它動態庫, 並啟用JVM。JVM啟用之後會進行一些初始化工作,之後產生BootstrapLoader,該Class Loader是由C++寫的。BootstrapLoader載入Launcher.java中的ExtClassLoader,並設定其Parent為 null,意思是ExtClassLoader的Parent Class Loader就是BootstrapLoader。然後BootstrapLoader又載入Launcher.java中的
AppClassLoade,並設定其Parent Class Loader是ExtClassLoader。不過如果調用ExtClassLoader的getParent( )方法,則返回的是null。這兩個Class Loader都是以靜態類的形式存在,並且都是用Java編寫的。這三個Class Loader它們都有自己的類尋找路徑:BootstrapLoader: sun.boot.class.pathExtClassLoader: java.ext.dirsAppClassLoader: java.class.path以上三個路徑都是Java的系統屬性,可以通過System.getProperty(String key)方法來查看其設定:System.out.println(System.getProperty("java.class.path")); 現在看看每一種屬性輸出後的值,從而可以發現每種Class Loader分別負責哪些類的裝載:key: sun.boot.class.pathString: %JAVA_HOME%/lib/resources.jar, rt.jar, sunrsasign.jar, jsse.jar, jce.jar, charsets.jar,          %JAVA_HOME%/classes key: java.ext.dirsString: %JAVA_HOME%/lib/ext, %Windows%/sun/java/lib/ext key: java.class.pathString: 程式的入口檔案類所在的目錄 由此可見,BootstrapLoader負責Java核心類(所有以java.*開頭的類)。ExtClassLoader負責載入擴充類(所以以javax.*開頭的類以及存在ext目錄下的類)。AppClassLoader負責載入應用程式自身的類。  第二部分:Java的類載入機制。Java是如何載入類的,其流程。類載入按照載入時機,是否自動載入分為兩種:積極式載入和按需載入。積極式載入的類是在JVM啟動之後,應用程式運行之前。至少包含rt.jar中的所有類。按需載入則是在應用程式運行之後,在程式運行過程中,JVM遇到一個還未被裝載的類,這時由Class Loader把該類載入記憶體。 類載入按照方式來分,也是兩種:隱式載入和明確式載入。隱式載入是通過new的方式,在類初始化時由JVM根據相應的Class Loader將類載入。明確式載入則是程式員在代碼中顯式利用某個Class Loader將類載入。 JVM自動裝載類的演算法是這樣的:如果Class A的執行個體引用了Class B的執行個體,則在預設情況下,JVM會先找到Class A的Class Loader,然後用該Class Loader來裝載Class B。 Class Loader裝載類的一般演算法如下:Background: Class Loader是按照層次關係組織起來的,每一個Class Loader都有一個Parent。如果在建立Class Loader時不顯式指定其父Class Loader,JVM會把系統Class Loader指定為該Class Loader的Parent。每一個Class Loader都有自己對應的Loaded Class Cache,換句話說,Loaded Class Cache由兩部分組成:ClassLoader,以及由它載入的Class類名。
  1. 檢查這個類是否已經被載入進去了
  2. 如果還沒有載入,調用父物件載入該類
  3. 如果父物件無法載入,調用本對象的findClass()取得這個類。
所以當建立自己的Class Loader時,只需要重載findClass()方法。Java 1.2之後,類的裝載採用委託模式。

一個已經載入的類是無法被更新的,如果試圖用同一個ClassLoader再次載入 同一個類,會得到異常java.lang.LinkageError: duplicate class definition。只能夠重新建立一個新的ClassLoader執行個體來再次載入新類。

  第三部分:定義自己的Class Loader為什麼要使用自己的ClassLoader?
因為JVM內建的ClassLoader只是懂得從本地檔案系統載入標準的java class檔案,如果編寫自己的ClassLoader,可以
  1. 在執行非置信代碼之前,自動驗證數位簽章
  2. 動態地建立符合使用者特定需要的定製化構建類
  3. 從特定的場所取得java class,例如資料庫和網路。

當建立自己的ClassLoader時,需要繼承java.lang.ClassLoader或者它的子類。在執行個體化每個ClassLoader對 象時,需要指定一個父物件;如果沒有的話,系統自動指定ClassLoader.getSystemClassLoader()為父物件。

第四部分:程式中顯示載入並執行個體化類的幾種方式:1) 使用Class類Class foo = Class.forName(String ClassTypeName);  // 通過調用ClassLoa der.getCallerClassLoader( )得到當前Class Loader,然後尋找並載入ClassTypeName。orClass foo = Class.forName(String ClassTypeName, boolean initialize, ClassLoader loader)  // 顯式指定用哪個Class Loader來尋找並載入ClassTypeName。 ClassTypeName boo = (ClassTypeName) foo.newInstance( ); 2) 通過ClassLoader的子類針對不同情況裝載類,比如java.net.URLClassLoader等。 擷取當前ClassLoader的方法:ClassLoader foo = Thread.currentTread().getCoontextClassLoader();

第五部分: Class.forName()與ClassLoader.loadClass()的區別 

Class clazz = Class.forName("XXX.XXX");

ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("XXX.XXX");
都可以裝載一個類那麼他們的區別是什麼呢?
進一步研究Class.forName()是調用
Class.forName(name, initialize, loader); 也就是Class.forName("XXX.XXX"); 等同與 Class.forName("XXX.XXX", true, CALLCLASS.class.getClassLoader());

第二次參數表示裝載類的時候是否初始化該類, 即調用類的靜態塊的語句及初始化靜態成員變數。

Class clazz = cl.loadClass("XXX.XXX"); 沒有指定是否初始化的選項。只有執行clazz.newInstance();時才能夠初始化類。可以說 Class.forName("XXX.XXX", false, cl)執行過程是一致的。只是ClassLoader.loadClass()是更底 層的操作。

看一下JDBC驅動的裝載。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbcurl");
當調用Class.forName("com.mysql.jdbc.Driver");是Driver已經被初始化並註冊到DriverManager中。MySQL Driver的代碼
public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{

public Driver()
throws SQLException
{
}

static 
{
try
{
DriverManager.registerDriver(new Driver());
}
catch(SQLException E)
{
throw new RuntimeException("Can't register driver!");
}
}
}
改修JDBC驅動的裝載
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("com.mysql.jdbc.Driver");
clazz.newInstance();
Connection conn = DriverManager.getConnection("jdbcurl");
同樣可以執行。

進一步說:

Class .forName是從指定的classloader中裝載類,如果沒有指定,也就是一個參數的時候,是從裝載當前對象執行個體所在的classloader中裝載類. 
而ClassLoader的執行個體調用loadclass方法,是指從當前ClassLoader執行個體中調用類,而這個執行個體與裝載當前所在類執行個體的Classloader也許不是同一個 . 
舉個例子吧, 有A,B , C兩個ClassLoader , 當前啟動並執行類D的執行個體是d(裝載它的是A) ,  如果D中使用Class.forName那麼就是使用的ClassLoader就是A,當然,也可以指定為B. 而如果D中代碼找到的ClassLoader執行個體是C,那麼就是用D來裝載所指定的類. 

為什麼要用不同的ClassLoader 裝載?
舉例來說:如果在Class 被載入的過程中,你希望使用在自己的Class Loader來實現特定的操作,請使用ClassLoader方式。 

貌似CGLib之類的bytecode generation架構很多地方會使用指定特殊ClassLoader的方式。
使用多個classloader的情況非常常見,比如說我們的app server,那麼都是這樣的. 在Web與EJB間, 他們的classLoader就是不同的,這樣做的目的就是為了避免兩者間類裝載的相互幹擾.

再舉個例子:
Static初始化區塊在什麼時候被調用的問題?
Public A{Static{System.out.println(“HaHaHa”);}}
Class.forName(“A”);
Class.forName(“A”,false, ClassLoader.getSystemClassLoader());
看看奧妙在哪裡?Java ClassLoader機制和原理又是如何? 
程式樣本:
public class A {
static { System.out.println("A`static is executed!");}
public A() {System.out.println("A`construct is executed!");}
public void show(){System.out.println("A`method is executed!");}

調用程式1:
Class c = Class.forName("A");
Method m = c.getMethod("show", new Class[0]);
System.out.println("A`test is executed!");
Object obj = c.newInstance();
m.invoke(obj, new Object[0]); 
執行結果:
A`static is executed!
A`test is executed!
A`construct is executed!
A`method is executed!
調用程式2:
Class c = ClassLoader.getSystemClassLoader().loadClass("A");
System.out.println("A`test is executed!");
Method m = c.getMethod("show", new Class[0]);
Object obj = c.newInstance();
m.invoke(obj, new Object[0]); 
執行結果:
A`test is executed!
A`static is executed!
A`construct is executed!
A`method is executed!
可見執行順序為先執行 static{}塊中的代碼,然後執行建構函式,之後才是方法的調用。
classloader的兩種載入方式:
1)pre-loading預先載入,載入基礎 類 
2)load-on-demand按需求載入 
java動態載入class的兩種方式: 
1)implic it隱式,即利用執行個體化才載入的特性來動態載入class 
2)explic it顯式方式,又分兩種方式: 
a)java.lang.Class的forName()方法  (上述調用程式1採用此方式載入)
b)java.lang.ClassLoader的loadClass()方法(上述調用程式2採用此方式載入)
static塊在什麼時候執行? 
當調用forName(String)載入class時執行,( 這個過程在類的所有父類中遞迴地調用) 
如果調用ClassLoader.loadClass並不會執行.
forName(String,false,ClassLoader)時也不會執行. 
如果載入Class時沒有執行static塊則在第一次執行個體化時執行.比如new ,Class.newInstance()操作 
static塊僅執行一次

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.