Java類加載器層次結(jié)構(gòu)原理解析
類加載器的層次結(jié)構(gòu):
引導(dǎo)類加載器(bootstrap class loader)
用來加載java的核心庫(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path路徑下的內(nèi)容),是用原生代碼來實現(xiàn)的(C實現(xiàn)的),并不繼承自java.lang.ClassLoader。
加載擴展類和應(yīng)用程序類加載器,并指定它們的父類加載器。
擴展類加載器(extensions class loader)
用來加載java的擴展庫(JAVA_HOME/jre/lib/ext/*.jar,或java.ext.dirs路徑下的內(nèi)容)java虛擬機的實現(xiàn)會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載java類。
有sun.miscLauncher$ExtClassLoader實現(xiàn),繼承自java.lang.ClassLoader
應(yīng)用程序類加載器(application class loader)
它根據(jù)java應(yīng)用的類路徑(classpath,java.class.path路徑)來加載指定路徑的類,一般來說,java應(yīng)用的類都是由它來完成加載的
由sun.misc.Launcher$AppClassLoader實現(xiàn),繼承自java.lang.ClassLoader
自定義類加載器
開發(fā)人員可以通過繼承java.lang.ClassLoader類的方式實現(xiàn)自己的類加載器,以滿足一些特殊的需求。
說明:在java中由于類的加載采用的是雙親委托機制,上面幾種類加載器是父子關(guān)系,其中引導(dǎo)類加載器為基礎(chǔ)。
ClassLoader類介紹
作用:
java.lang.ClassLoader類的基本職責(zé)就是根據(jù)一個指定的類的名稱找到或者生成其對應(yīng)的字節(jié)代碼,然后從這些字節(jié)代碼中定義出一個java類,即java.lang.Class類的一個實例。
除此之外,ClassLoader還負(fù)責(zé)加載java應(yīng)用所需的資源文件,如圖像文件和配置文件等。
相關(guān)方法:
- getParent() 返回該類加載器的父類加載器
- loadClass(String name) 加載名稱為name的類,返回的結(jié)果是java.lang.Class類的實例
- findClass(String name) 查找名稱為name的類,返回的結(jié)果是java.lang.Class類的實例
- findLoadedClass(String name) 查找名稱為name的已經(jīng)被加載過的類,返回的結(jié)果是java.lang.Class類的實例
- defineClass(String name,byte[] b,int off,int len) 把字節(jié)數(shù)組b中的內(nèi)容轉(zhuǎn)換成java類,返回的結(jié)果是java.lang.Class類的實例。這個方法被聲明為final的。
- resolveClass(Class<?> c) 鏈接指定的java類。
代碼測試類加載器:
public class Demo02 {
public static void main(String[] args) {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());;
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());;
}
}
輸出:
sun.misc.Launcher$AppClassLoader@1016632
sun.misc.Launcher$ExtClassLoader@dc6a77
null
依次為應(yīng)用加載器、擴展加載器和引導(dǎo)加載器(但是引導(dǎo)加載為原生代碼所寫,因此獲取不到,為null)。
類加載器的代理模式:
代理模式:交給其他加載器來加載指定的類。
雙親委托機制:
就是某個特定的類加載器在接到加載類的請求時,首先將加載任務(wù)委托給父類加載器,以此追溯,直到最高的爺爺輩的,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時,才自己去加載。
雙親委托機制是為了保證java核心庫的類型安全(這種機制就保證不會出現(xiàn)用戶自己能定義java.lang.Object類的情況)。
類加載器除了用于加載類,也是安全的最基本的屏障。
雙親委托機制是代理模式的一種:
并不是所有的類加載器都采用雙親委托機制。
tomcat服務(wù)器類加載器也使用代理模式,所不同的是它是首先嘗試自己去加載某個類,如果找不到再代理給父類加載器。這與一般類加載器的順序是相反的。
自定義類加載器的流程:
繼承:java.lang.ClassLoader
首先檢查請求的類型是否已經(jīng)被這個類裝載器裝載到命名空間中,如果已經(jīng)裝載,則返回
委派類將加載請求給父類加載器,如果父類加載器能夠完成,則返回父類加載器加載的Class實例
調(diào)用本類加載器的findClass()方法,師徒獲取對應(yīng)的字節(jié)碼,如果獲取得到,則調(diào)用defineClass()導(dǎo)入類型到方法區(qū);如果獲取不到對應(yīng)的字節(jié)碼或者其它原因失敗,則返回異常給loadClass(),loadClass()轉(zhuǎn)拋異常,終止加載過程
注:被兩個加載器加載的同一個類,Jvm不認(rèn)為是相同的類。
示例代碼如下:
package com.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定義文件系統(tǒng)加載器
* @author We.lxk
*
*/
public class FileSystemClassLoader extends ClassLoader{
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
private byte[] getClassData(String classname){ //com.test.User -> rootDir/com/test/User
String path = rootDir +"/"+classname.replace(".", "/")+".class";
//IOUtils 可以使用它將讀取的流數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
byte[] buffer = new byte[1024];
int temp = 0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null)
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(baos!=null)
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//應(yīng)該先查詢有沒有加載過這個類。已經(jīng)加載,則直接返回加載好的類。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try{
//System.out.println("hello");
c = parent.loadClass(name); //委派給父類加載
}catch(Exception e){
//e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0, classData.length);
}
}
}
return c;
}
}
測試代碼:
package com.test;
/**
* 測試自定義的FileSystemClassLoader
* @author We.lxk
*
*/
public class Demo03 {
public static void main(String[] args) throws Exception {
FileSystemClassLoader loader = new FileSystemClassLoader("D:/myJava");
FileSystemClassLoader loader2 = new FileSystemClassLoader("D:/myJava");
Class<?> c = loader.loadClass("com.test.Demos");
Class<?> c2 = loader.loadClass("com.test.Demos");
Class<?> c3 = loader2.loadClass("com.test.Demos");
Class<?> c4 = loader2.loadClass("java.lang.String");
Class<?> c5 = loader.loadClass("com.test.Demo");
System.out.println(c.hashCode()+" "+c.getClassLoader());
System.out.println(c2.hashCode()+" "+c2.getClassLoader());
System.out.println(c3.hashCode()+" "+c3.getClassLoader());
System.out.println(c4.hashCode()+" "+c4.getClassLoader());
System.out.println(c5.hashCode()+" "+c5.getClassLoader());
//System.out.println(.getClassLoader());
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
最新hadoop安裝教程及hadoop的命令使用(親測可用)
這篇文章主要介紹了最新hadoop安裝教程(親測可用),本文主要講解了如何安裝hadoop、使用hadoop的命令及遇到的問題解決,需要的朋友可以參考下2022-06-06
MyBatis 如何配置多個別名 typeAliasesPackage
這篇文章主要介紹了MyBatis 如何配置多個別名 typeAliasesPackage,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Java使用Lambda表達(dá)式查找list集合中是否包含某值問題
Java使用Lambda表達(dá)式查找list集合中是否包含某值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
實例詳解Spring Boot實戰(zhàn)之Redis緩存登錄驗證碼
本章簡單介紹redis的配置及使用方法,本文示例代碼在前面代碼的基礎(chǔ)上進(jìn)行修改添加,實現(xiàn)了使用redis進(jìn)行緩存驗證碼,以及校驗驗證碼的過程。感興趣的的朋友一起看看吧2017-08-08
Java語言實現(xiàn)簡單FTP軟件 FTP軟件效果圖預(yù)覽之下載功能(2)
這篇文章主要為大家詳細(xì)介紹了Java語言實現(xiàn)簡單FTP軟件,F(xiàn)TP軟件效果圖預(yù)覽之下載功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03
SpringBoot自定義Starter實現(xiàn)流程詳解
SpringBoot中的starter是一種非常重要的機制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要在maven中引入starter依賴,SpringBoot就能自動掃描到要加載的信息并啟動相應(yīng)的默認(rèn)配置。starter讓我們擺脫了各種依賴庫的處理,需要配置各種信息的困擾2022-09-09

