Java classloader類加載器的實(shí)現(xiàn)
Classloader的繼承性。
第一級(jí)叫做bootstrap classloader,加載JDK自帶的類,也就是${JAVA_HOME}/lib下的類。
第二級(jí)叫做extern classloader,加載${JAVA_HOME}/lib/ext下的類。
第三級(jí)叫做system classloader,加載第三方的類,就是classpath里其他的類。
第四級(jí)及更高級(jí)叫做plugin classloader,是用戶自己寫的classloader或者new出來(lái)的URLClassLoader,加載classpath以外的類。在tomcat容器中,每個(gè)應(yīng)用里的WEB-INF/lib和WEB-INF/classes就是通過(guò)tomcat自定義的classloader去加載的。
Classloader的隔離性。
- 父級(jí)不能調(diào)用子級(jí)的類。
比如extern classloader里加載的class不能調(diào)用system classloader加載的類,會(huì)拋class not found異常. - 同一級(jí)不同classloader加載的類不能互相調(diào)用。
比如有兩個(gè)plugin classloader, loader A 與loader B。loader A加載的類class A不能調(diào)用loader B加載的類 class B. - 無(wú)法獲取bootstrap Classloader的實(shí)例。
System.out.println(String.class.getClassLoader());
這個(gè)輸出結(jié)果是null
總而言之,只能child加載的類調(diào)用parent加載的類。兄弟及各種遠(yuǎn)房親戚不能互相調(diào)用。
簡(jiǎn)單的plugin classloader
直接使用jdk自帶的url classloader就可以加載任意jar包里的類了。
比如以下代碼
final URL url = new URL("file:///C:/Users/Ryan/IdeaProjects/learn/classloader/heap-1.0.jar");
final URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
final Class<?> heapClass = classLoader.loadClass("net.cloudsun.head.SmallHeap");
final Constructor<?> constructor = heapClass.getConstructor();
final Object o = constructor.newInstance();
final Method add = heapClass.getMethod("add", java.lang.Comparable.class);
for (int i = 0; i < 10; i++) {
add.invoke(o, -i);
}
System.out.println(o);
自定義classloader
當(dāng)不能提供url時(shí),比如jar包不在磁盤里,也不在http服務(wù)器上,或者jar包是加密的。只能自己寫classloader進(jìn)行類的加載。
自己寫classloader最終必須繼承Classloader類。因?yàn)檫@個(gè)類有個(gè)保護(hù)型且final的defineClass方法,傳入類的字節(jié)碼,也就是byte[],就可以加載一個(gè)類。也就是說(shuō)class的來(lái)源是byte[]。
比如
public class JarClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 從jar包里取byte數(shù)組
try {
JarFile jarFile = new JarFile("heap-1.0.jar");
final byte[] bytes = JarUtils.getByte(jarFile, name);
if (bytes == null) {
return super.loadClass(name);
}
return super.defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
上述代碼的JarUtils是一個(gè)自己寫的工具類。
public static byte[] getByte(JarFile jarFile, String className) {
final String entry = className.replace('.', '/') + ".class";
final JarEntry jarEntry = jarFile.getJarEntry(entry);
if (jarEntry == null) {
return null;
}
try (final InputStream inputStream = jarFile.getInputStream(jarEntry)) {
return IOUtils.toByteArray(inputStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
當(dāng)然這個(gè)classloader寫得并不規(guī)范。因?yàn)橐?guī)范的classloader首先要調(diào)用parent classloader去加載類,如果parent加載失敗,再自己加載,而且加載的class必須緩存起來(lái)。因?yàn)閺淖止?jié)碼加載class的開銷是非常巨大的。當(dāng)然前三級(jí)JDK自帶的的classloader都會(huì)在loadClass方法里去檢查class是否已經(jīng)加載。所以只要每級(jí)class loader優(yōu)先調(diào)用parent classloader,就可以自動(dòng)實(shí)現(xiàn)class的緩存。
到此這篇關(guān)于Java classloader類加載器的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java classloader內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
劍指Offer之Java算法習(xí)題精講二叉樹的構(gòu)造和遍歷
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
基于Feign傳輸對(duì)象無(wú)法接收參數(shù)的問(wèn)題
這篇文章主要介紹了基于Feign傳輸對(duì)象無(wú)法接收參數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java類庫(kù)BeanUtils組件使用方法及實(shí)例詳解
這篇文章主要介紹了Java類庫(kù)BeanUtils組件使用方法級(jí)實(shí)例詳解,需要的朋友可以參考下2020-02-02
SpringBoot熱部署和整合Mybatis的過(guò)程
熱部署,就是在應(yīng)用正在運(yùn)行的時(shí)候升級(jí)軟件,卻不需要重新啟動(dòng)應(yīng)用,本文給大家詳細(xì)介紹SpringBoot熱部署和整合Mybatis的過(guò)程,感興趣的朋友跟隨小編一起看看吧2023-10-10

