自定義類加載器的父類為何是AppClassLoader說明
首先有一個(gè)概念要了解
我們通常說在jdk中,默認(rèn)有三個(gè)類加載器,bootstrapClassLoader、ExtClassLoader、AppClassLoader,我們說前者是后者的父類加載器,但是實(shí)際上,這里所謂的父類加載器,并不是Java中的父子類繼承關(guān)系,而是說:
- AppClassLoader中有一個(gè)parentClassLoader設(shè)置的值是ExtClassLoader
- ExtClassLoader中的parentClassLoader設(shè)置的是bootstrapClassLoader
- 我們自定義的類加載器,對(duì)應(yīng)的parentClassLoader是AppClassLoader
我們?nèi)绻远x一個(gè)類加載器,默認(rèn)設(shè)置的父類加載器是AppClassLoader,這個(gè)原因是在因?yàn)樵诔跏蓟远x類加載器的時(shí)候,會(huì)指定其parentClassLoader為AppClassLoader
Launcher
這個(gè)類是jre中用來啟動(dòng)main()方法的入口,在這個(gè)類中,我們著重關(guān)注的是初始化構(gòu)造方法
public Launcher() { ? ? Launcher.ExtClassLoader var1; ? ? try { ? ? ?? ?// 初始化extClassLoader ? ? ? ? var1 = Launcher.ExtClassLoader.getExtClassLoader(); ? ? } catch (IOException var10) { ? ? ? ? throw new InternalError("Could not create extension class loader", var10); ? ? } ? ? try { ? ? ?? ?// 初始化appClassLoader,這里的這個(gè)賦值是比較重要的 ? ? ? ? this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); ? ? } catch (IOException var9) { ? ? ? ? throw new InternalError("Could not create application class loader", var9); ? ? } ? ? Thread.currentThread().setContextClassLoader(this.loader); ? ? String var2 = System.getProperty("java.security.manager"); ? ? if (var2 != null) { ? ? ? ? SecurityManager var3 = null; ? ? ? ? if (!"".equals(var2) && !"default".equals(var2)) { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); ? ? ? ? ? ? } catch (IllegalAccessException var5) { ? ? ? ? ? ? } catch (InstantiationException var6) { ? ? ? ? ? ? } catch (ClassNotFoundException var7) { ? ? ? ? ? ? } catch (ClassCastException var8) { ? ? ? ? ? ? } ? ? ? ? } else { ? ? ? ? ? ? var3 = new SecurityManager(); ? ? ? ? } ? ? ? ? if (var3 == null) { ? ? ? ? ? ? throw new InternalError("Could not create SecurityManager: " + var2); ? ? ? ? } ? ? ? ? System.setSecurityManager(var3); ? ? } }
這個(gè)構(gòu)造方法中,我目前所了解到的,就是初始化了AppClassLoader和ExtClassLoader,并且,和這篇博客相關(guān)的,我們只需要關(guān)心this.loader這個(gè)全局變量,這個(gè)全局變量存放的是AppClassLoader的對(duì)象信息
構(gòu)造一個(gè)自定義類加載器
我們要自定義一個(gè)類加載器,只需要繼承classLoader,并重寫findClass()方法即可
class MyClassLoader extends ClassLoader { ? ? private String loadPath; ? ? public String getLoadPath() { ? ? ? ? return loadPath; ? ? } ? ? public void setLoadPath(String loadPath) { ? ? ? ? this.loadPath = loadPath; ? ? } ? ? @Override ? ? protected Class<?> findClass(String name) throws ClassNotFoundException { ? ? ? ? try { ? ? ? ? ? ? byte[] bytes = loadByte(name); ? ? ? ? ? ? return defineClass(name, bytes, 0, bytes.length); ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? System.out.println(e.getMessage()); ? ? ? ? } ? ? ? ? return null; ? ? } ? ? private byte[] loadByte(String className) throws Exception { ? ? ? ? className = className.replaceAll("\\.", "/"); ? ? ? ? FileInputStream fileInputStream = new FileInputStream(loadPath + "/" + className + ".class"); ? ? ? ? int available = fileInputStream.available(); ? ? ? ? byte[] data = new byte[available]; ? ? ? ? fileInputStream.read(data); ? ? ? ? fileInputStream.close(); ? ? ? ? return data; ? ? } }
那么,我在使用的時(shí)候,只需要調(diào)用new MyClassLoaderTest()即可
new MyClassLoaderTest()
我們說,自定義的類解析器對(duì)應(yīng)的parentClassLoader,就是在空參構(gòu)造函數(shù)中被賦值的
因?yàn)镸yClassLoaderTest繼承了ClassLoader,所以,會(huì)調(diào)用到ClassLoader的空參構(gòu)造函數(shù)
protected ClassLoader() { ? ? this(checkCreateClassLoader(), getSystemClassLoader()); }
private ClassLoader(Void unused, ClassLoader parent) { ? ? this.parent = parent; ? ? if (ParallelLoaders.isRegistered(this.getClass())) { ? ? ? ? parallelLockMap = new ConcurrentHashMap<>(); ? ? ? ? package2certs = new ConcurrentHashMap<>(); ? ? ? ? domains = ? ? ? ? ? ? Collections.synchronizedSet(new HashSet<ProtectionDomain>()); ? ? ? ? assertionLock = new Object(); ? ? } else { ? ? ? ? // no finer-grained lock; lock on the classloader instance ? ? ? ? parallelLockMap = null; ? ? ? ? package2certs = new Hashtable<>(); ? ? ? ? domains = new HashSet<>(); ? ? ? ? assertionLock = this; ? ? } }
我們會(huì)發(fā)現(xiàn),parentClassLoader就是getSystemClassLoader()返回的,
java.lang.ClassLoader#getSystemClassLoader ?? ?java.lang.ClassLoader#initSystemClassLoader // 這個(gè)方法中的其他變量我們可以暫時(shí)先不關(guān)心,我們看到有獲取到一個(gè)Launcher對(duì)象 private static synchronized void initSystemClassLoader() { ? ? if (!sclSet) { ? ? ? ? if (scl != null) ? ? ? ? ? ? throw new IllegalStateException("recursive invocation"); ? ? ? ? sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); ? ? ? ? if (l != null) { ? ? ? ? ? ? Throwable oops = null; ? ? ? ? ? ? // 在這里調(diào)用其getClassLoader()方法,將返回的值,賦值給scl,而這個(gè)scl就是入?yún)⒅械膒arent ? ? ? ? ? ? scl = l.getClassLoader(); ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? scl = AccessController.doPrivileged( ? ? ? ? ? ? ? ? ? ? new SystemClassLoaderAction(scl)); ? ? ? ? ? ? } catch (PrivilegedActionException pae) { ? ? ? ? ? ? ? ? oops = pae.getCause(); ? ? ? ? ? ? ? ? if (oops instanceof InvocationTargetException) { ? ? ? ? ? ? ? ? ? ? oops = oops.getCause(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if (oops != null) { ? ? ? ? ? ? ? ? if (oops instanceof Error) { ? ? ? ? ? ? ? ? ? ? throw (Error) oops; ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? // wrap the exception ? ? ? ? ? ? ? ? ? ? throw new Error(oops); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? sclSet = true; ? ? } }
這里的getClassLoader()返回的就是上面我說明的,需要特別關(guān)注的this.classLoader這個(gè)全局變量
scl是從l.getClassLoader()這個(gè)方法獲取到的結(jié)果,那我們看下這個(gè)方法
sun.misc.Launcher#getClassLoader public ClassLoader getClassLoader() { ? ? ? ? return this.loader; }
可以看到,這里的getClassLoader,就是博客上面 Launcher 這個(gè)方法中賦值的this.loader;
所以,通過上面的代碼,可以發(fā)現(xiàn),我們自定義的類解析器,是在初始化的時(shí)候,指定了parent為AppClassLoader
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
完美解決MybatisPlus插件分頁(yè)查詢不起作用總是查詢?nèi)繑?shù)據(jù)問題
這篇文章主要介紹了解決MybatisPlus插件分頁(yè)查詢不起作用總是查詢?nèi)繑?shù)據(jù)問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08手動(dòng)模擬JDK動(dòng)態(tài)代理的方法
這篇文章主要介紹了手動(dòng)模擬JDK動(dòng)態(tài)代理的方法,幫助大家更好的了解和學(xué)習(xí)Java 代理的相關(guān)知識(shí),感興趣的朋友可以了解下2020-11-11實(shí)例解析Java設(shè)計(jì)模式編程中的適配器模式使用
本篇文章主要通過實(shí)例對(duì)適配器模式進(jìn)行了詳解,需要的朋友可以參考下2017-04-04關(guān)于@Scheduled參數(shù)及cron表達(dá)式解釋
這篇文章主要介紹了關(guān)于@Scheduled參數(shù)及cron表達(dá)式解釋,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Docker?快速部署Springboot項(xiàng)目超詳細(xì)最新版
這篇文章主要介紹了Docker?快速部署Springboot項(xiàng)目超詳細(xì)最新版的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04springboot 接收List 入?yún)⒌膸追N方法
本文主要介紹了springboot 接收List 入?yún)⒌膸追N方法,本文主要介紹了7種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03idea在運(yùn)行期間,實(shí)現(xiàn)讓修改的頁(yè)面實(shí)時(shí)生效
這篇文章主要介紹了idea在運(yùn)行期間,實(shí)現(xiàn)讓修改的頁(yè)面實(shí)時(shí)生效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01如何基于java隨機(jī)獲取不重復(fù)數(shù)值
這篇文章主要介紹了如何基于java隨機(jī)獲取不重復(fù)數(shù)值,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09IDEA 如何控制編輯左側(cè)的功能圖標(biāo)ICON(操作步驟)
很多朋友被idea左側(cè)的圖標(biāo)不見了這一問題搞的焦頭爛額,不知道該怎么操作,今天小編就交大家如何控制編輯左側(cè)的功能圖標(biāo) ICON,文字內(nèi)容不多,主要通過兩張截圖給大家說明,感興趣的朋友一起看看吧2021-05-05