《深入理解 AOP》

一、AOP 是什么

AOP(Aspect Oriented Programming),即面向切面編程,是軟件開發中一種重要的編程范式。它通過橫向抽取機制,將那些與業務邏輯本身無關、卻為業務模塊所共同調用的邏輯或責任(如事務處理、日志管理、權限校驗等)封裝起來,然后通過“動態植入”的方式嵌入到業務邏輯的指定位置,從而實現業務邏輯的隔離與解耦。

AOP 是面向對象編程(OOP)的補充。在 OOP 中,我們通過類和繼承來組織代碼,但在某些情況下,會遇到一些問題。例如,當需要為多個不具有繼承關系的對象添加公共方法時,如日志記錄、性能監控等,如果采用 OOP 的方式,就需要在每個對象中都添加相同的方法,這會導致大量的重復代碼,增加維護成本。而 AOP 則可以很好地解決這個問題,它將這些公共邏輯抽取出來,集中管理,避免了重復代碼的產生。

二、AOP 的優勢

  1. 減少重復代碼:將公共邏輯集中到切面中,避免了在多個地方重復編寫相同的代碼。

  2. 提高開發效率:開發者可以專注于業務邏輯的實現,而無需在每個地方都處理那些公共的、與業務邏輯無關的邏輯。

  3. 方便維護:當需要修改公共邏輯時,只需修改切面中的代碼,而無需修改每個使用該邏輯的地方。

三、AOP 的技術要點

(一)通知(Advice)

通知定義了“什么時候”和“做什么”。它包含了需要用于多個應用對象的橫切行為。根據通知的執行時機,可以分為以下幾種類型:

  1. 前置通知(@Before):在目標方法調用之前調用通知。

  2. 后置通知(@After):在目標方法完成之后調用通知。

  3. 環繞通知(@Around):在被通知的方法調用之前和調用之后執行自定義的方法。需要注意的是,目標對象的方法需要手動執行。

  4. 返回通知(@AfterReturning):在目標方法成功執行之后調用通知。

  5. 異常通知(@AfterThrowing):在目標方法拋出異常之后調用通知。

(二)連接點(Join Point)

連接點是程序執行過程中能夠應用通知的所有點。在 Spring 中,連接點指的是方法,因為 Spring 只支持方法類型的連接點。

(三)切點(Pointcut)

切點定義了在“什么地方”進行切入,哪些連接點會得到通知。切點表達式用于明確指定方法的返回類型、類名、方法名和參數名等與方法相關的部件。常用的切點表達式格式為:execution([修飾符] 返回值類型 包名.類名.方法名(參數))。其中,修飾符可以省略,返回值類型、包名、類名、方法名和參數都可以使用通配符(*..)來表示。

(四)切面(Aspect)

切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——是什么、何時、何地完成功能。

(五)引入(Introduction)

引入允許我們向現有的類中添加新方法或者屬性。

(六)織入(Weaving)

織入是把切面應用到目標對象并創建新的代理對象的過程。織入分為編譯期織入、類加載期織入和運行期織入。

四、AOP的底層原理

一、AOP 的底層原理概述

在 Spring 框架中,AOP 的實現依賴于動態代理技術。動態代理技術允許在運行時動態地創建代理對象,并在代理對象上調用方法時插入額外的邏輯(即通知)。Spring AOP 主要使用兩種動態代理技術:JDK 動態代理和 CGLIB 代理。

二、JDK 動態代理技術

JDK 動態代理是 Java 提供的一種標準代理機制,它依賴于 Java 的反射機制。JDK 動態代理的核心是 java.lang.reflect.Proxy 類和 InvocationHandler 接口。以下是 JDK 動態代理的實現步驟:

(一)為接口創建代理類的字節碼文件

  1. 定義接口:首先,需要定義一個接口,目標對象和代理對象都將實現這個接口。例如:

    public interface UserService {void save();
    }
  2. 實現目標類:目標類實現了上述接口,并提供了具體的業務邏輯。例如:

    ?
    public class UserServiceImpl implements UserService {@Overridepublic void save() {System.out.println("業務層:保存用戶...");}
    }
  3. 創建代理類:通過 java.lang.reflect.Proxy 類動態生成代理類。代理類實現了與目標類相同的接口,并在方法調用時插入額外的邏輯。例如:

    public class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在目標方法執行前插入邏輯System.out.println("前置通知:記錄日志");// 執行目標方法Object result = method.invoke(target, args);// 在目標方法執行后插入邏輯System.out.println("后置通知:記錄日志");return result;}
    }
  4. 生成代理實例:通過 Proxy.newProxyInstance 方法動態生成代理實例。例如:

    UserService userService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), // 目標類的類加載器new Class<?>[]{UserService.class}, // 目標類實現的接口new MyInvocationHandler(new UserServiceImpl()) // 自定義的 InvocationHandler
    );

(二)使用 ClassLoader 將字節碼文件加載到 JVM

  1. 類加載器的作用ClassLoader 負責加載字節碼文件到 JVM 中。在 JDK 動態代理中,Proxy.newProxyInstance 方法會使用目標類的類加載器來加載生成的代理類。

  2. 動態生成字節碼Proxy 類會在運行時動態生成代理類的字節碼,并通過類加載器加載到 JVM 中。代理類的字節碼是基于目標類實現的接口動態生成的。

(三)創建代理類實例對象,執行對象的目標方法

  1. 代理類實例:通過 Proxy.newProxyInstance 方法生成的代理類實例對象,可以像普通對象一樣調用接口方法。

  2. 方法調用:當調用代理類實例的方法時,實際上會調用 InvocationHandlerinvoke 方法。在 invoke 方法中,可以插入前置通知、后置通知等邏輯,并最終調用目標方法。

三、CGLIB 代理技術

CGLIB(Code Generation Library)是一個強大的字節碼生成庫,它可以在運行時動態生成目標類的子類,并覆蓋目標類的方法。CGLIB 代理主要用于那些沒有實現接口的類,或者需要代理類的方法而不是接口的方法。

(一)CGLIB 代理的基本原理

  1. 動態生成子類:CGLIB 通過字節碼操作庫(如 ASM)動態生成目標類的子類。生成的子類繼承了目標類,并覆蓋了目標類的方法。

  2. 方法攔截:在覆蓋的方法中,CGLIB 提供了一個 MethodInterceptor 接口,用于攔截方法調用。在攔截器中,可以插入額外的邏輯,并最終調用目標方法。

(二)CGLIB 代理的實現步驟

  1. 定義目標類:目標類不需要實現接口,例如:

    public class UserServiceImpl {public void save() {System.out.println("業務層:保存用戶...");}
    }
  2. 創建攔截器:實現 MethodInterceptor 接口,定義攔截邏輯。例如:

    ?
    public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在目標方法執行前插入邏輯System.out.println("前置通知:記錄日志");// 執行目標方法Object result = proxy.invokeSuper(obj, args);// 在目標方法執行后插入邏輯System.out.println("后置通知:記錄日志");return result;}
    }
  3. 生成代理實例:通過 Enhancer 類動態生成代理實例。例如:

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserServiceImpl.class); // 設置目標類
    enhancer.setCallback(new MyMethodInterceptor()); // 設置攔截器
    UserServiceImpl userService = (UserServiceImpl) enhancer.create();
  4. 使用代理實例:通過代理實例調用方法時,實際上會調用攔截器的 intercept 方法,在其中插入額外的邏輯,并最終調用目標方法。

四、JDK 動態代理與 CGLIB 代理的比較

表格
特性JDK 動態代理CGLIB 代理
適用場景目標類必須實現接口目標類可以沒有接口
代理方式通過接口代理通過生成子類代理
性能相對較好,因為基于接口調用稍差,因為需要生成子類并覆蓋方法
靈活性只能代理接口方法可以代理類的方法,更靈活
實現機制基于 Java 反射和?InvocationHandler基于字節碼操作庫(如 ASM)

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

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

相關文章

Python【協程(Coroutine)和線程的關系】

協程(Coroutine)和線程都是實現并發編程的技術&#xff0c;但它們在實現方式、使用場景和性能上有顯著區別。理解它們的關系與差異有助于在實際應用中選擇合適的并發模型&#xff0c;以下是它們的核心關系與對比分析&#xff1a; 一、核心關系 互補關系 協程和線程可以結合使用…

Springboot——Redis的使用

在當今的軟件開發領域&#xff0c;緩存技術是提升應用性能的關鍵手段之一。Redis 作為一款高性能的鍵值對存儲數據庫&#xff0c;憑借其出色的讀寫速度和豐富的數據結構&#xff0c;在緩存場景中得到了廣泛應用。Spring Boot 作為一款簡化 Spring 應用開發的框架&#xff0c;與…

BEVPoolv2:A Cutting-edge Implementation of BEVDet Toward Deployment

背景 該論文是在BEVDet的基礎上進行了一個調整優化&#xff0c;傳統的方法是將特征圖與深度預測進行外積得到視椎特征圖&#xff0c;再將它與預處理好的體素索引結合&#xff0c;將每個視椎特征分類到每個voxel中進行累加和的操作。BEVFusion與BEVDepth等方法是避免了累加和&a…

藍橋杯常考的找規律題

目錄 靈感來源&#xff1a; B站視頻鏈接&#xff1a; 找規律題具有什么樣的特點&#xff1a; 報數游戲&#xff08;Java組&#xff09;&#xff1a; 題目描述&#xff1a; 題目鏈接&#xff1a; 思路詳解&#xff1a; 代碼詳解&#xff1a; 階乘求和&#xff08;Java組…

使用ffmpeg 將圖片合成為視頻,填充模糊背景,并添加兩段音樂

1.輸入3張圖片,每張播放一次,播放兩秒,視頻分辨率設置為1920:1080,每張圖片前0.3秒淡入,后0.3秒淡出,圖片寬高比不變,用白色填充空白區域 ffmpeg -loop 1 -t 2 -i "img1.jpg" \-loop 1 -t 2 -i "img2.jpg" \-loop 1 -t 2 -i "img3.jpg" \-filte…

PostgreSQL技術內幕29:事件觸發器tag原理解析

文章目錄 0.簡介1.概念說明2.tag的生成和存儲2.1 tag合法性校驗2.2 內存中存儲2.3 持久化存儲 3.tag的觸發 0.簡介 在上一篇文章中中&#xff0c;我們介紹了PG中的兩種觸發器&#xff0c;即適合于DML的普通觸發器和對于DDL的事件觸發器&#xff0c;其中事件觸發器與常規的 DML…

mysql 導入很慢,如何解決

精選 原創 碼出財富2025-04-14 17:35:14博主文章分類&#xff1a;數據庫©著作權 文章標簽mysql數據庫用戶名文章分類MySQL數據庫yyds干貨盤點閱讀數184 導入大型 SQL 文件到 MySQL 數據庫時&#xff0c;速度可能會受到影響。以下是一些優化方法和建議&#xff0c;幫助你…

多物理場耦合低溫等離子體裝置求解器PASSKEy2

文章目錄 PASSKEy2簡介PASSKEY2計算流程PASSKEy2 中求解的物理方程電路模型等離子體模型燃燒模型 PASSKEy2的使用 PASSKEy2簡介 PASSKEy2 是在 PASSKEy1 的基礎上重新編寫的等離子體數值模擬程序。 相較于 PASSKEy1&#xff0c; PASSKEy2 在具備解決低溫等離子體模擬問題的能力…

保姆級zabbix監控jmx、數據庫和網絡監控(SNMP)

前言 在當今數字化時代&#xff0c;企業IT基礎設施的穩定性與性能直接關系到業務連續性和用戶體驗。隨著系統復雜性的不斷增加&#xff0c;單一維度的監控已難以滿足全面運維需求。Zabbix作為一款功能強大的開源監控解決方案&#xff0c;通過整合JMX&#xff08;Java Manageme…

復雜地形越野機器人導航新突破!VERTIFORMER:數據高效多任務Transformer助力越野機器人移動導航

作者&#xff1a; Mohammad Nazeri 1 ^{1} 1, Anuj Pokhrel 1 ^{1} 1, Alexandyr Card 1 ^{1} 1, Aniket Datar 1 ^{1} 1, Garrett Warnell 2 , 3 ^{2,3} 2,3, Xuesu Xiao 1 ^{1} 1單位&#xff1a; 1 ^{1} 1喬治梅森大學計算機科學系&#xff0c; 2 ^{2} 2美國陸軍研究實驗室&…

SharpMap與TerraLib:C#與C++開源GIS庫

大家好&#xff0c;今天為大家介紹的軟件是SharpMap&#xff1a;一款專為了C#&#xff08;.NET&#xff09;環境設計的開源地圖和空間數據處理庫&#xff1b;TerraLib&#xff1a;一款由C編寫、支持多種數據庫的開源的GIS軟件庫。 下面&#xff0c;我們將從兩個開源軟件的主要…

音視頻學習 - MP3格式

環境 JDK 13 IDEA Build #IC-243.26053.27, built on March 16, 2025 Demo MP3Parser MP3 MP3全稱為MPEG Audio Layer 3&#xff0c;它是一種高效的計算機音頻編碼方案&#xff0c;它以較大的壓縮比將音頻文件轉換成較小的擴展名為.mp3的文件&#xff0c;基本保持源文件的音…

Unity中數據和資源加密(異或加密,AES加密,MD5加密)

在項目開發中&#xff0c;始終會涉及到的一個問題&#xff0c;就是信息安全&#xff0c;在調用接口&#xff0c;或者加載的資源&#xff0c;都會涉及安全問題&#xff0c;因此就出現了各種各樣的加密方式。 常見的也是目前用的最廣的加密方式&#xff0c;分別是&#xff1a;DE…

部署本地deepseek并在調用的詳細步驟以及解決一些可能出現的問題(Windows,Linux, WSL)

打開Ollama官網&#xff1a;https://ollama.com/ 直接下載Ollama并且安裝好Ollama、這時候就能看到app里多了個ollama&#xff0c;但是我們不用打開它 打開Windows Powershell&#xff1a; ollama run deepseek-r1:1.5b 7b 8b 14b 32b 70b 根據自己的電腦配置和需求更換不同的…

【KWDB 創作者計劃】_嵌入式硬件篇---寄存器與存儲器截斷與溢出

文章目錄 前言一、寄存器與存儲器1. 定義與基本概念寄存器(Register)位置功能特點存儲器(Memory)位置功能特點2. 關鍵區別3. 層級關系與協作存儲層次結構協作示例4. 為什么需要寄存器性能優化指令支持減少總線競爭5. 其他寄存器類型專用寄存器程序計數器(PC)棧指針(SP)…

小白自學python第二天

學習python的第二天 一、判斷語句 1、布爾類型和比較運算符 1、布爾類型 表示現實生活中的邏輯&#xff0c;真&#xff08;True&#xff0c;用數字1表示&#xff09;和假&#xff08;False&#xff0c;用數字0表示&#xff09; 2、布爾類型變量的定義 變量的名稱 布爾類…

linux基礎操作1------(文件命令)

一.前言 我們本章開始講解linux&#xff0c;我們對于linux得有重要的認識&#xff0c;比如項目部署等等&#xff0c;都會用到linux&#xff0c;今天我們就開始linux的學習&#xff0c;我們需要準備的工具有vmware和xshell&#xff0c;而這里我就不教大家虛擬機的安裝以及xshel…

編碼問題整合

一、windows系統編碼 查看編碼命令&#xff1a;chcp - 936 GBK - 65001 UTF-8 - 437 英文修改系統編碼 1、控制面板修改 需管理員權限-Windows 10/11進入 控制面板 > 區域 > 管理 > 更改系統區域設置勾選 Beta版: 使用Unicode UTF-8提供全球語言支持 → 重啟生效修…

如何配置Spark

1.上傳spark安裝包到某一臺機器&#xff08;自己在finaShell上的機器&#xff09;。 2.解壓。 把第一步上傳的安裝包解壓到/opt/module下&#xff08;也可以自己決定解壓到哪里&#xff09;。對應的命令是&#xff1a;tar -zxvf 安裝包 -C /opt/module 3.重命名。進入/opt/mo…

Redis 完整配置模板

一、基礎連接配置&#xff08;單機模式&#xff09; 基礎參數&#xff08;適用Spring Boot&#xff09; spring:redis:host: 127.0.0.1port: 6379password: your_passworddatabase: 0 # 默認DB索引timeout: 2000ms # 全局操作超時時間二、連接池參數&#xff08;通用核心配…