熱點(diǎn)推薦:Java類加載器深入理解
首先回顧一下,java虛擬機(jī)載入java類的步驟:java文件經(jīng)過(guò)編譯器編譯后變成字節(jié)碼文件(.class文件),類加載器 (ClassLoader)讀取.class文件,并且轉(zhuǎn)換成java.lang.Class的一個(gè)實(shí)例,***通過(guò)newInstance方法創(chuàng)建該類的 一個(gè)對(duì)象。ClassLoader的作用就是根據(jù)一個(gè)類名,找到對(duì)應(yīng)的字節(jié)碼,根據(jù)這些字節(jié)碼定義出對(duì)應(yīng)的類,該類就是java.lang.Class的 一個(gè)實(shí)例。
類加載器的組織結(jié)構(gòu)
java有三個(gè)初始類加載器,當(dāng)java虛擬機(jī)啟動(dòng)時(shí),它們會(huì)按照以下順序啟動(dòng):Bootstrap classloader -> extension classloader -> system classloader。三者的關(guān)系:bootstrap classloader是extension classloader的parent,extension classloader是system classloader的parent。
bootstrap classloader
它是最原始的類加載器,并不是由java代碼寫的,是由原生代碼編寫的。Java有一次編譯、所有平臺(tái)運(yùn)行的效果,就是因?yàn)樗鼘懥艘环莨δ芟嗤?,但針?duì)不同平臺(tái)不同語(yǔ)言實(shí)現(xiàn)的底層代碼。它負(fù)責(zé)加載java核心庫(kù),大家可運(yùn)行以下代碼,看看自己本地的java核心庫(kù)在哪里:
- URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
- for (int i = 0; i < urls.length; i++) {
- System.out.println(urls[i].toExternalForm());
- }
本人的運(yùn)行結(jié)果:
- file:/home/eric/jdk1.6.0_35/jre/lib/resources.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/rt.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/sunrsasign.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/jsse.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/jce.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/charsets.jar
- file:/home/eric/jdk1.6.0_35/jre/lib/modules/jdk.boot.jar
- file:/home/eric/jdk1.6.0_35/jre/classes
extension classloader
它用來(lái)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或java.ext.dirs系統(tǒng)屬性指定的)JAR的類包。注意,因?yàn)樗莃ootstrap classloader加載的,所以當(dāng)你運(yùn)行:
ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
輸出的是:the parent of extension classloader : null
system classloader
它用于加載classpath目錄下的jar包,我們寫的java類,一般都是由它加載,除非你自己制定個(gè)人的類加載器。
全盤負(fù)責(zé)委托機(jī)制
classloader加載類時(shí),使用全盤負(fù)責(zé)委托機(jī)制,可以分開兩部分理解:全盤負(fù)責(zé),委托。
全盤負(fù)責(zé)機(jī)制:若類A調(diào)用了類B,則類B和類B所引入的所有jar包,都由類A的類加載器統(tǒng)一加載。
委托機(jī)制:類加載器在加載類A時(shí),會(huì)優(yōu)先讓父加載器加載,當(dāng)父加載器加載不到,再找父父加載器,一直找到bootstrap classloader都找不到,才自己去相關(guān)的路徑去尋找加載。以下是ClassLoader的源碼:
- protected synchronized Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // First, check if the class has already been loaded
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- //從父加載器加載
- c = parent.loadClass(name, false);
- } else {
- //從bootstrap loader加載
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
舉個(gè)例子,類加載器加載類A的過(guò)程:
1,判斷是否已經(jīng)加載過(guò),在cache里面查找,若有,跳7;否則下一步
2,判斷當(dāng)前加載器是否有父加載器,若無(wú),則當(dāng)前為ext classloader,跳去4;否則下一步
3,請(qǐng)求父加載器加載該類,若加載成功,跳7;若不成功,即父加載器不能找到該類,跳2
4,請(qǐng)求jvm的bootstrap classloader加載,若加載成功,跳7;若失敗,跳5
5,當(dāng)前加載器自己加載,若成功,跳7;否則,跳6
6,拋出ClassNotFoundException
7,返回Class
編寫自己的類加載器
Java加載類的過(guò)程,實(shí)質(zhì)上是調(diào)用loadClass()方法,loadClass中調(diào)用findLoadedClass()方法來(lái)檢查該類是否 已經(jīng)被加載過(guò),如果沒(méi)有就會(huì)調(diào)用父加載器的loadClass(),如果父加載器無(wú)法加載該類,就調(diào)用findClass()來(lái)查找該類。
所以我們要做的就是新建MyClassLoader繼承java.lang.ClassLoader,重寫其中的findClass()方法。主要是重新設(shè)計(jì)查找字節(jié)碼文件的方案,然后調(diào)用definedClass來(lái)返回。
本人寫了一個(gè)demo,用自己的類加載器去加載指定java文件,且?guī)в袩岵渴鹦Ч?,具體請(qǐng)查看以下url。
Demo地址:http://git.oschina.net/ericquan8/hot-deploy