類的動態加載-雙親委派模型

java反射基礎

Java 基礎 - 反射機制詳解 | Java 全棧知識體系 (pdai.tech)

類的動態加載

參考鏈接:類的動態加載

構造是和實例化也就是對象相關的。

靜態代碼塊是在初始化的時候就調用的 Class.forName();就會調用靜態代碼塊

forName,加載類時默認初始化

        Class.forName();    //默認初始化
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class.forName("Person",false,classLoader);//不進行初始化

類加載器的研究

類加載器,加載類時默認不初始化。

        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class<?> person = classLoader.loadClass("Person");Class<?> person = Class.forName("Person", false, classLoader); //兩個代碼作用相同

底層的原理,實現加載任意的類

java雙親委派

image-20240704094900997

Class<?> person1 = classLoader.loadClass("Person");處打個斷點進行調試。

首先調用ClassLoader.classLoader(a)因為AppClassLoader中的classLoader參數是兩個,所以調用到了其父類ClassLoader.classLoader(a,b)

之后ClassLoader.classLoader(a,b)調用AppClassLoader.classLoader(a), —> ClassLoader.classLoader(a,b)

在ClassLoader.classLoader(a,b)中就涉及到了雙親委派模型

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //檢查類有沒有被加載if (c == null) {                                //類沒有被加載 進入long t0 = System.nanoTime();try {if (parent != null) {                   //還有父加載器的話,讓父加載器loadClass   這里也就是ExtClassLoaderc = parent.loadClass(name, false);} else {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.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

進入ExtClassLoader.loadCLass, 因為其中沒有loadCLass所以又直接調用到了,上面CLass.loadClass方法。

這次

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {              //找不到父加載器了,因為bootstrap ClassLoader 不在java中c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);  //走到這一步 也不回找到 因為是一個普通的類 不會調用BootstrapClassLoader去加載}} 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.long t1 = System.nanoTime();c = findClass(name);              //之后走到findClass("Person") 因為最后是在App CLassLoader中加載的 所以ExtClassLoader中先不跟了 返回的是null// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

之后return c=null邏輯又回到了 AppCLassLoader的loadCLass

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //檢查類有沒有被加載if (c == null) {                                //類沒有被加載 進入long t0 = System.nanoTime();try {if (parent != null) {                   //還有父加載器的話,讓父加載器loadClass   這里也就是ExtClassLoaderc = parent.loadClass(name, false);  //ExtCLassLoader返回null。所以c=null} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {                           //因為C=null 進入// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);                   //進到findClass("Person") 跟一下這里// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

下面跟進findClass(“Person”)

因為AppClassLoader總沒有findClass方法,所以找到了其父類URLClassLoader的findClass

protected Class<?> findClass(final String name)throws ClassNotFoundException
{final Class<?> result;try {result = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {public Class<?> run() throws ClassNotFoundException {String path = name.replace('.', '/').concat(".class");Resource res = ucp.getResource(path, false);          //ucp是類的路徑 URLClassPath類if (res != null) {                                    //res不為空 進入try {return defineClass(name, res);                //主要跟一下defindClass} catch (IOException e) {throw new ClassNotFoundException(name, e);}} else {return null;}}}, acc);} catch (java.security.PrivilegedActionException pae) {throw (ClassNotFoundException) pae.getException();}if (result == null) {throw new ClassNotFoundException(name);}return result;
}

下圖可以觀察到 AppClassLoader調用findCLass時,ucp(查找路徑)里面加入了file:/H:/java_des/target/classes/(我們項目Class路徑),所以可以查到Person類,res不為空。

image-20240704135445797

跟一下URLCLassLoader.defineClass

image-20240704140107786

return 調用的是URLClassLoader的父類SecureClassLoader的defineClass方法

image-20240704140249660

return 調用的是CLassLoader的defineClass方法

protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)     //這里name是類名,b是字節碼throws ClassFormatError
{protectionDomain = preDefineClass(name, protectionDomain);String source = defineClassSourceLocation(protectionDomain);Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); //在defineClass1完成類的加載(字節碼) 是個native類postDefineClass(c, protectionDomain);return c;
}

之后一層一層返回加載的類,加載到了URLCLass.findClass中return defineClass(name, res);處。

下一步,也就是最終返回到了我們寫的loadClass方法調用代碼處,可以看到返回了Person類

在這里插入圖片描述

總結

ClassLoader -> SecureClassLoader -> URLCLassLoader -> AppClassLoader (繼承關系 父->子)

ClassLoader.loadClass -> URLCLass.findClass(重寫方法)(路徑中能找到類才進入defineCLass) ->SecureClassLoader.defineClass(從字節碼加載類)->ClassLoader.defineClass

image-20240704144522395

利用

先編寫并編譯一個彈計算器的代碼,放到一個指定路徑,之后把項目中的Test.class刪除(項目路徑沒有Test.class,看看是否能夠通過類加載器的利用,找到類)

import java.io.IOException;public class Test {static  {  //靜態代碼塊try {Runtime.getRuntime().exec("calc");} catch (IOException e) {e.printStackTrace();}}
}

URLCLassLoader 任意類加載:file/http/jar 協議

public class LoadClassTest {public static void main(String[] args) throws Exception {URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///G:\\Java反序列化\\class_test\\")}); //指定Class的查找路徑URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999/")}); //指定Class的查找路徑URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://127.0.0.1:9999/Test.jar!/")}); //指定Class的查找路徑Class<?> c = urlClassLoader.loadClass("Test"); //load Person類c.newInstance();  //實例化}
}

ClassLoader.defineClass 字節碼加載任意類 私有

public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);defineClass.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class c = (Class) defineClass.invoke(classLoader, "Test",code, 0, code.length);  //對象 類名 字節碼 字節碼起始 字節碼長度 defineClass返回的是Class<?> 這里也就是返回的Test.classc.newInstance(); //實例化 觸發靜態代碼塊}
}

Unsafe.defineClass 字節碼加載 public類但是不能直接調用,需要先反射調用public方法實例化類

Unsafe類中defineClass方法是public的,但是是個單例模式,不能直接調用defineClass()。

看到有個getUnsafe方法,是public的,但是直接調用Unsafe.getUnsafe()是會報錯的因為有個安全檢查。

最后找到theUnsafe屬性

private static final Unsafe theUnsafe = new Unsafe();

所以反射調用theUnsafe屬性去實例化Unsafe

public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);
public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class unsafe = Unsafe.class;Field theUnsafeField = unsafe.getDeclaredField("theUnsafe");theUnsafeField.setAccessible(true);Unsafe unsafe1 = (Unsafe) theUnsafeField.get(null);Class<?> test = unsafe1.defineClass("Test", code, 0, code.length, classLoader, null);test.newInstance();}
}

Spring里面可以直接生成

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/39481.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/39481.shtml
英文地址,請注明出處:http://en.pswp.cn/web/39481.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

你們叫AI,我們叫DI

大家好&#xff0c;才是真的好。 最近Notes/Domino產品在做哪些更新&#xff0c;想必大家都很好奇。 從2022年年末到現在&#xff0c;快兩年了&#xff0c;任何一個有追求的大企業或巨頭&#xff0c;應該都在追求實現一件事情&#xff1a;AI人工智能。 從小道消息來看&#…

深度學習之OpenCV的DNN模塊

OpenCV的DNN&#xff08;Deep Neural Network&#xff09;模塊是一個強大的工具&#xff0c;允許開發者在計算機視覺應用中使用深度學習模型。該模塊支持多種深度學習框架和模型格式&#xff0c;并提供了高效的推理能力。以下是對OpenCV DNN模塊的詳細介紹&#xff0c;包括其功…

Unity實現等弧長的曲線滾動列表

Unity實現等弧長的曲線滾動列表 在Unity中實現等弧長的曲線滾動列表通常涉及到曲線路徑生成、物理模擬以及動畫控制。首先&#xff0c;你需要創建一個可滾動的UI元素&#xff08;如List或ScrollView&#xff09;&#xff0c;并將其錨點設置在一個可以跟隨曲線移動的位置。以下…

掃描工具Metasploit的安裝和使用

安裝 Metasploit 你可以使用 Metasploit 的安裝腳本從 Rapid7 官方站點下載安裝。 更新系統包&#xff1a; sudo apt update下載并安裝 Metasploit&#xff1a; curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framew…

【高級篇】分區與分片:MySQL的高級數據管理技術(十三)

引言 在上一章,我們探討了MySQL的主從復制與高可用性,這是構建健壯數據庫架構的基石。現在,讓我們深入到更高級的主題——分區與分片,這些技術對于處理大規模數據集和提升數據庫性能至關重要。我們將詳細介紹表分區的概念、類型及分片技術的應用,為下一章討論MySQL集群與…

小程序怎么跳轉到其他小程序里

小程序跳轉到其他小程序里&#xff0c;可以通過微信小程序提供的API實現。以下是幾種常見的跳轉方式&#xff0c;以及相應的實現方法&#xff1a; 1. 直接跳轉 使用API&#xff1a; wx.navigateToMiniProgram 參數說明&#xff1a; appId&#xff08;string類型&#xff0c…

Idea2024安裝后點擊無響應

問題 最近因工作需要&#xff0c;升級一下 idea 版本&#xff0c;之前一直使用的是2020版本&#xff0c;下載最新的2024版本&#xff08;下載的 zip 包免安裝模式&#xff0c;之前使用的2020版本也是免安裝的&#xff0c;因為是免安裝的&#xff0c;所以之前的版本也沒有刪除&…

解決Vue3中路由頁面跳轉出現白屏,刷新頁面之后展示正常的問題

遇到這個問題&#xff0c;首先需要檢查根組件標簽最外層是否包含了個最大的div盒子來包裹內容。如下圖所示&#xff1a; 我的項目就是因為沒有將兩塊內容放到一個大盒子里面&#xff0c;所以才會出現白屏的問題。然后我去查了相關的資料&#xff0c;了解到這個問題是Vue組件渲染…

TSINGSEE智能分析網關V4人員區域徘徊AI檢測:算法原理介紹及技術應用場景

一、引言 在現代社會&#xff0c;隨著科技的不斷發展&#xff0c;視頻監控系統已廣泛應用于各個領域&#xff0c;如公共安全、商業管理、交通監控等。其中&#xff0c;區域徘徊檢測算法作為一種重要的視頻分析技術&#xff0c;能夠有效地識別出特定區域內人員的徘徊行為&#…

Spring Cloud Alibaba - Sentinel 分布式系統流量哨兵

目錄 概述特征基本概念 安裝Sentinel微服務引入Sentinel案例流控規則&#xff08;流量控制&#xff09;流控模式-直接流控模式-關聯流控模式-鏈路流控效果-快速失敗流控效果-預熱WarmUp流控效果-排隊等候 流控規則&#xff08;并發線程數控制&#xff09;熔斷規則&#xff08;熔…

AndroidStudio的switch-case語句報錯解決

大家好&#xff0c;我是咕嚕鐵蛋。在Android開發的道路上&#xff0c;我們總會遇到各種各樣的問題&#xff0c;其中之一就是switch-case語句的報錯。今天&#xff0c;我就和大家分享一下在AndroidStudio中遇到switch-case語句報錯時&#xff0c;我們應該如何排查和解決這些問題…

Kotlin 處理livedata數據倒灌

LiveData 數據倒灌問題通常指的是在訂閱者注冊后立即接收到之前發送的數據。這個行為在某些場景下是需要的&#xff0c;但在某些情況下可能是不希望的。 主要有兩種中方法&#xff1a; 1 使用 SingleLiveEvent--------SingleLiveEvent 是一個自定義的 LiveData 類&#xff0c…

精雕細琢:Postman中請求體的設置藝術

精雕細琢&#xff1a;Postman中請求體的設置藝術 在API測試與開發的廣闊天地中&#xff0c;Postman以其強大的功能和用戶友好的界面成為了探索這一領域的必備工具。而在構建API請求的過程中&#xff0c;請求體&#xff08;Body&#xff09;的設置無疑是傳達數據給服務器的關鍵…

Django 安裝 Zinnia 后出現故障

在Django中安裝和配置Zinnia時遇到故障可能有多種原因&#xff0c;通常包括版本兼容性、依賴關系或配置問題。這里提供一些常見的解決方法和調試步驟&#xff0c;幫助大家解決問題。 首先&#xff0c;確保您安裝的Zinnia版本與Django版本兼容。查看Zinnia的官方文檔或GitHub頁…

Linux庫概念及相關編程(動態庫-靜態庫)

Linux庫概念及相關編程 分文件編程案例 分文件編程是指將程序按功能模塊劃分成不同的文件進行編寫&#xff0c;這種方法有以下好處&#xff1a; 功能責任劃分&#xff1a;每個文件對應一個功能模塊&#xff0c;職責明確&#xff0c;易于理解和維護。方便調試&#xff1a;可以…

三、c++ qt 實現一個基于tcp的Session

在Qt框架下實現一個基于TCP的Session管理,你可以利用Qt的網絡模塊QTcpServer和QTcpSocket。下面是一個簡單的示例,展示了如何建立一個服務器,接收客戶端連接,并為每個連接的客戶端創建一個Session對象來管理會話。 首先,你需要包含必要的Qt頭文件,并定義一個TcpSession類…

C++左值/右值/左值引用/右值引用

1&#xff09;C入門級小知識&#xff0c;分享給將要學習或者正在學習C開發的同學。 2&#xff09;內容屬于原創&#xff0c;若轉載&#xff0c;請說明出處。 3&#xff09;提供相關問題有償答疑和支持。 左值和右值的概念&#xff1a; 早期的c語言中關于左值和右值的定義&a…

Kithara常見問題解答

目錄 通用問題我的內核驅動程序已經簽名了嗎&#xff1f;是否可以在打開驅動程序時防止顯示介紹窗口&#xff1f;Windows 7 仍然支持嗎&#xff1f;錯誤0x10142422(KSERROR_CANNOT_START_KERNEL)在KS_openDriver時出現&#xff1f;錯誤 10145241 (KSERROR_CANNOT_START_KERNEL)…

低代碼開發技術助力企業數字化管理的實踐探究

隨著信息技術的飛速發展&#xff0c;企業對于數字化管理的需求日益迫切。而低代碼開發技術&#xff0c;以其高效、靈活、易用的特點&#xff0c;正逐漸成為企業數字化管理的重要工具。本文將進一步探討低代碼開發技術在企業數字化管理實踐中的應用及其帶來的變革。 低代碼開發技…

每日一題——Python實現PAT乙級1026 程序運行時間(舉一反三+思想解讀+逐步優化)五千字好文

一個認為一切根源都是“自己不夠強”的INTJ 個人主頁&#xff1a;用哲學編程-CSDN博客專欄&#xff1a;每日一題——舉一反三Python編程學習Python內置函數 Python-3.12.0文檔解讀 目錄 我的寫法 代碼結構和邏輯 時間復雜度 空間復雜度 代碼優化建議 總結 我要更強 …