自定义ClassLoader和双亲委派机制
轉載自?自定義ClassLoader和雙親委派機制
?
ClassLoader
ClassLoad:類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。Java 源程序(.java 文件)在經過 Java 編譯器編譯之后就被轉換成 Java 字節代碼(.class 文件)。類加載器負責讀取 Java 字節代碼,并轉換成 java.lang.Class 類的一個實例。
雙親委派機制
某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。
自己對JVM中的ClassLoader和雙親委派機制的一些理解:
- java虛擬機中的class其實都是通過classloader來裝載的
- 只有當你使用該class的時候才會去裝載,一個classloader只會裝載同一個class一次。
- 不同的類加載器的實例所加載的字節碼文件,其通過反射獲取的對象不是相同類型(相互賦值會拋出類型強轉異常)。即:判斷兩個類是否為同一對象的標準里面有一條是類加載器必須為相同。
- 雙親委派機制能在很大程度上防止內存中出現多個相同的字節碼文件。
- 在加載類的時候默認會使用當前類的ClassLoader進行加載(類A中引用了類B,JVM會用類A的類加載器加載類B)。
- 在線程中加載一個類的時候:當前線程的類加載器可以通過Thread類的getContextClassLoader()獲得,也可以通過setContextClassLoader()自己設置類加載器(PS:自己沒有試驗過)。
ClassLoader體系結構圖
ClassLoader體系結構圖
package com.tzx.reflection;public class MyClassLoader {public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getContextClassLoader());System.out.println(ClassLoaderTest.class.getClassLoader());System.out.println(System.class.getClassLoader());System.out.println(ClassLoader.getSystemClassLoader());System.out.println(ClassLoader.getSystemClassLoader().getParent());System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());}}運行結果:
sun.misc.Launcher$AppClassLoader@4aa298b7 sun.misc.Launcher$AppClassLoader@4aa298b7 null sun.misc.Launcher$AppClassLoader@382f3bf0 sun.misc.Launcher$ExtClassLoader@25082661 null按照雙親委派機制加載類,每當需要加載一個新的類時,當前的類加載器會先委托其父加載器,查詢有沒有加載該類。如果父類加載器已近加載該類,那么直接返回加載的class對象,如果沒有那么繼續向上尋找父類加載器,如果在祖宗類加載器Bootstrap都沒有加載該類,那么需要當前的類加載器自己加載,如果當前的類加載器也不能加載則會跑出ClassNotFoundException異常 (PS:類加載器沒有向下尋找,沒有getChild只有getParent)。
用這種思想去解析上邊代碼:Thread.currentThread().getContextClassLoader()指出當前的類加載器是AppClassLoader,需要加載MyClassLoader.class先在父類加載器(ExtClassLoader)中尋找,沒有再向祖宗類加載中尋找(Bootstrap ClassLoader),還沒有找到那么AppClassLoader自己去加載。
JVM中的類加載器類型:
- (Bootstrap ClassLoader)啟動類加載器:
負責加載java_home/jar/lib/rt.jar目錄下的核心類或- Xbootclasspath指定目錄下的類。由于引導類加載器全部是native代碼來實現的并且涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進行操作,從上面的ClassLoader體系結構圖中可以看出在java代碼中獲取啟動類加載器為null。
rt.jar.png
像System.java這樣的由系統提供的類都在rt.jar中,由Bootstrap ClassLoader加載,由于Bootstrap類加載器不是Java寫的,所以打印出來的類名為null。
- (Extension)擴展類加載器:負責加載java_home/lib/ext目錄下的擴展類或 -Djava.ext.dirs 指定目錄下的類。 開發者可以直接使用標準擴展類加載器。
我們將第一段代碼生產的MyClassLoader.class文件打包成jar(java打包成jar|執行jar包中的main方法),放在java_home/jar/lib/ext目錄下。
ext.png
再次執行該java程序
im@58user:/usr/lib/jvm/jdk1.8.0_101/jre/lib/ext$ java -cp MyClassLoader.jar com.loadclass.demo.ClassLoaderTest start sun.misc.Launcher$AppClassLoader@55f96302 sun.misc.Launcher$ExtClassLoader@70dea4e null sun.misc.Launcher$AppClassLoader@55f96302 sun.misc.Launcher$ExtClassLoader@70dea4e null通過終端輸出結果我們可以看到執行ClassLoaderTest程序的類加載器是AppClassLoader,但加載ClassLoaderTest類的類加載器是ExtClassLoader。因為java_home/jar/lib/ext/.jar在執行程序之前就被ExtClassLoader類加載器加載過了。這樣避免了類的重復加載~!~!*
- (System)類加載器:負責加載-classpath/-Djava.class.path所指的目錄下的類。開發者可以直接使用標準擴展類加載器。一般來說,Java應用的類都是由他來完成加載的。
除了以上三種類型的類加載器,還有一個中比較特殊的:線程上下文類加載器(PS:暫時沒做這方面的記錄)。
自定義類加載器
- 定義
- 使用
總結
以上是生活随笔為你收集整理的自定义ClassLoader和双亲委派机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何超越你的转换标准-采访Convert
- 下一篇: jQuery 基础教程 (三)之jQue