歡迎您光臨本站 註冊首頁

Java classloader的體系結構

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  java classLoader 體系結構(轉)

  原文出處:http://blog.chenlb.com/2009/06/java-classloader-architecture.html

  Bootstrap ClassLoader/啟動類載入器

  主要負責jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工作.

  Extension ClassLoader/擴展類載入器

  主要負責jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作.

  System ClassLoader/系統類載入器

  主要負責java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作.

  User Custom ClassLoader/用戶自定義類載入器(java.lang.ClassLoader的子類)

  在程序運行期間, 通過java.lang.ClassLoader的子類動態載入class文件, 體現java動態實時類裝入特性.

  類載入器的特性:

  每個ClassLoader都維護了一份自己的名稱空間, 同一個名稱空間里不能出現兩個同名的類.

  為了實現java安全沙箱模型頂層的類載入器安全機制, java默認採用了 " 雙親委派的載入鏈 " 結構.

  類圖中, BootstrapClassLoader是一個單獨的java類, 其實在這裡, 不應該叫他是一個java類.因為,它已經完全不用java實現了.它是在jvm啟動時, 就被構造起來的, 負責java平台核心庫.

  自定義類載入器載入一個類的步驟

  ClassLoader 類載入邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類載入器載入流程:

  // 檢查類是否已被裝載過

  Class c = findLoadedClass(name);

  if (c == null ) {

  // 指定類未被裝載過

  try {

  if (parent != null ) {

  // 如果父類載入器不為空, 則委派給父類載入

  c = parent.loadClass(name, false );

  } else {

  // 如果父類載入器為空, 則委派給啟動類載入載入

  c = findBootstrapClass0(name);

  }

  } catch (ClassNotFoundException e) {

  // 啟動類載入器或父類載入器拋出異常后, 當前類載入器將其

  // 捕獲, 並通過findClass方法, 由自身載入

  c = findClass(name);

  }

  }

  線程上下文類載入器

  java默認的線程上下文類載入器是 系統類載入器(AppClassLoader).

  // Now create the class loader to use to launch the application

  try {

  loader = AppClassLoader.getAppClassLoader(extcl);

  } catch (IOException e) {

  throw new InternalError(

  "Could not create application class loader" );

  }

  // Also set the context class loader for the primordial thread.

  Thread.currentThread().setContextClassLoader(loader);

  Java代碼

  // Now create the class loader to use to launch the application

  try {

  loader = AppClassLoader.getAppClassLoader(extcl);

  } catch (IOException e) {

  throw new InternalError(

  "Could not create application class loader" );

  }

  // Also set the context class loader for the primordial thread.

  Thread.currentThread().setContextClassLoader(loader);

  以上代碼摘自sun.misc.Launch的無參構造函數Launch().

  使用線程上下文類載入器, 可以在執行線程中, 拋棄雙親委派載入鏈模式, 使用線程上下文里的類載入器載入類.

  典型的例子有, 通過線程上下文來載入第三方庫jndi實現, 而不依賴於雙親委派.

  大部分java app伺服器(jboss, tomcat..)也是採用contextClassLoader來處理web服務.

  還有一些採用 hotswap 特性的框架, 也使用了線程上下文類載入器, 比如 seasar (full stack framework in japenese).

  線程上下文從根本解決了一般應用不能違背雙親委派模式的問題.

  使java類載入體系顯得更靈活.

  隨著多核時代的來臨, 相信多線程開發將會越來越多地進入程序員的實際編碼過程中. 因此,

  在編寫基礎設施時, 通過使用線程上下文來載入類, 應該是一個很好的選擇.

  當然, 好東西都有利弊. 使用線程上下文載入類, 也要注意, 保證多根需要通信的線程間的類載入器應該是同一個,

  防止因為不同的類載入器, 導致類型轉換異常(ClassCastException).

  為什麼要使用這種雙親委託模式呢?

  因為這樣可以避免重複載入,當父親已經載入了該類的時候,就沒有必要子ClassLoader再載入一次.

  考慮到安全因素,我們試想一下,如果不使用這種委託模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義類型,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因為String已經在啟動時被載入,用戶自定義類是無法載入一個自定義的ClassLoader.

  java動態載入class的兩種方式:

  implicit隱式,即利用實例化才載入的特性來動態載入class

  explicit顯式方式,又分兩種方式:

  java.lang.Class的forName()方法

  java.lang.ClassLoader的loadClass()方法

  用Class.forName載入類

  Class.forName使用的是被調用者的類載入器來載入類的.

  這種特性, 證明了java類載入器中的名稱空間是唯一的, 不會相互干擾.

  即在一般情況下, 保證同一個類中所關聯的其他類都是由當前類的類載入器所載入的.

  public static Class forName(String className)

  throws ClassNotFoundException {

  return forName0(className, true , ClassLoader.getCallerClassLoader());

  }

  /** Called after security checks have been made. */

  private static native Class forName0(String name, boolean initialize,

  ClassLoader loader)

  throws ClassNotFoundException;

  Java代碼

  public static Class forName(String className)

  throws ClassNotFoundException {

  return forName0(className, true , ClassLoader.getCallerClassLoader());

  }

  /** Called after security checks have been made. */

  private static native Class forName0(String name, boolean initialize,

  ClassLoader loader)

  throws ClassNotFoundException;

  上面中 ClassLoader.getCallerClassLoader 就是得到調用當前forName方法的類的類載入器

  static塊在什麼時候執行?

  當調用forName(String)載入class時執行,如果調用ClassLoader.loadClass並不會執行.forName(String,false,ClassLoader)時也不會執行.

  如果載入Class時沒有執行static塊則在第一次實例化時執行.比如new ,Class.newInstance()操作

  static塊僅執行一次

  各個java類由哪些classLoader載入?

  java類可以通過實例.getClass.getClassLoader()得知

  介面由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()獲得實例)載入

  ClassLoader類由bootstrap loader載入

  NoClassDefFoundError和ClassNotFoundException

  NoClassDefFoundError:當java源文件已編譯成.class文件,但是ClassLoader在運行期間在其搜尋路徑load某個類時,沒有找到.class文件則報這個錯

  ClassNotFoundException:試圖通過一個String變數來創建一個Class類時不成功則拋出這個異常


[火星人 ] Java classloader的體系結構已經有578次圍觀

http://coctec.com/docs/java/show-post-60526.html