Spring(七)AOP-代理模式

目錄

代理模式

一 靜態代理

一、核心作用

二、使用場景

二 動態代理

一、核心作用

二、使用場景

具體實現:(初始)

具體實現:(改進)

一、核心業務邏輯

1. 接口?MathCalculator

2. 實現類?MathCalculatorImpl

二、日志工具模塊

日志工具類?logUtils

三、動態代理模塊

動態代理類?DynamicProxy

四、測試驗證模塊

測試方法?test03()


概念:AOP 面向切面編程(Aspect-Oriented Programming)

面向切面編程,能夠將那些與業務無關,卻為業務模塊所調用的邏輯封裝起來,以減少系統代碼的重復度,減少模塊之間的耦合度。


代理模式

對比維度靜態代理動態代理
代理關系確定時機編碼期間:代理類需手動編寫,明確代理目標運行期間:代理類由程序動態生成,無需預定義
實現方式代理類與目標類實現相同接口(或繼承目標類),并通過組合持有目標對象通過反射(JDK動態代理)或字節碼生成(CGLIB)動態攔截目標對象方法
代理范圍- 僅能代理同一接口/父類的類
- 需為每個目標類單獨編寫代理類
-?JDK動態代理:代理接口實現類
-?CGLIB:代理無接口類(非final類)
代碼復雜度- 代碼簡單直觀,適合新手
- 需手動維護代理類
- 需理解反射/字節碼機制
- 框架(如Spring)可簡化開發
性能- 直接調用目標方法,無反射開銷
- 性能更高
-?JDK動態代理:反射調用性能較低
-?CGLIB:首次生成代理類較慢
靈活性- 擴展功能固定,僅針對特定類
- 接口變動需同步修改代理類
- 可批量代理多個類,統一添加功能(如日志、事務)
- 接口變動自動適配
典型應用場景- 代理少量固定類
- 高頻調用場景(如核心服務)
- 框架級功能擴展(Spring AOP)
- 需統一管理多個類的增強邏輯

靜態代理:編碼期間就決定好了代理關系

定義:代理對象是目標對象的子類型,代理對象本省并不是目標對象,而是將目標作為自己的屬性

優點:同一種類型的所有對象都能代理

缺點:范圍太小

動態代理:運行期間才決定好了代理關系

定義:目標對象會在執行期間被動態攔截,插入指定邏輯

優點:可以代理世間萬物

缺點:復雜不好寫

一 靜態代理

靜態代理是通過手動編寫一個“替身類”(代理類),在不修改原始類代碼的前提下,為其添加額外功能(如日志、權限校驗),使用時通過代理類間接調用原始類。

一、核心作用

  1. 控制訪問:限制或增強對原始對象的訪問(如權限校驗)。

  2. 功能擴展:在不修改原始對象代碼的前提下,添加新功能(如日志記錄、性能統計)。


二、使用場景

  1. 日志記錄:在方法調用前后自動記錄日志。

  2. 權限校驗:調用方法前檢查用戶權限。

  3. 延遲加載:在需要時才初始化復雜對象(如大文件加載)。

  4. 簡化復雜操作:隱藏底層復雜邏輯,對外提供簡潔接口。

  5. 避坑提示:如果系統中有大量需要代理的類,建議改用動態代理(如JDK動態代理或CGLIB)。

代碼實現:

定義接口:

package org.example.spring02.MathMethod;public interface MathCalculator {//定義四則運算public int div(int a, int b);public int mul(int a, int b);public int sub(int a, int b);public int add(int a, int b);}

實現類:
?

package org.example.spring02.MathMethod.MIm;import org.example.spring02.MathMethod.MathCalculator;
import org.springframework.stereotype.Component;@Component
public class MathCalculatorImpl implements MathCalculator {@Overridepublic int add(int a, int b) {return a + b;}@Overridepublic int sub(int a, int b) {return a - b;}@Overridepublic int mul(int a, int b) {return a * b;}@Overridepublic int div(int a, int b) {return a / b;}}

靜態代理類:

package org.example.spring02.MathMethod.proxy.statics;import lombok.Data;
import org.example.spring02.MathMethod.MathCalculator;/*** 靜態代理類,實現了MathCalculator接口* 本類用于在不修改原始類的情況下,增加額外的功能處理,如日志、權限校驗等
*/@Data
public class CalculatorStaticProxy implements MathCalculator {// 被代理對象private MathCalculator target;public CalculatorStaticProxy(MathCalculator mc) {this.target = mc;}@Overridepublic int div(int a, int b) {//書寫日志System.out.println("div被調用了,參數是:" + a + "," + b);System.out.println("div被調用了,結果是:" + target.div(a, b));return target.div(a, b);}@Overridepublic int mul(int a, int b) {System.out.println("mul被調用了,參數是:" + a + "," + b);System.out.println("mul被調用了,結果是:" + target.mul(a, b));return target.mul(a, b);}@Overridepublic int sub(int a, int b) {System.out.println("sub被調用了,參數是:" + a + "," + b);System.out.println("sub被調用了,結果是:" + target.sub(a, b));return target.sub(a, b);}@Overridepublic int add(int a, int b) {System.out.println("add被調用了,參數是:" + a + "," + b);System.out.println("add被調用了,結果是:" + target.add(a, b));return target.add(a, b);}
}

測試類:

    // JUnit測試方法,用于測試MathCalculator的功能@Testvoid test01() {// 創建MathCalculator的實例對象MathCalculator target = new MathCalculatorImpl();// 調用add方法進行加法運算,并打印結果int add1 = target.add(1, 3);System.out.println(add1);// 分隔符,用于區分不同的測試部分System.out.println("======");// 創建靜態代理對象,并傳入目標對象CalculatorStaticProxy calculatorStaticProxy = new CalculatorStaticProxy(target);// 通過靜態代理對象調用add方法進行加法運算,并打印結果int add = calculatorStaticProxy.add(1, 3);System.out.println(add);}

運行展示:

二 動態代理

動態代理:JDK動態代理目標對象必須有接口,代理的也只是接口規定的方法

動態代理:是在運行時通過反射或字節碼技術自動生成代理類,無需手動編寫“替身類”,即可為多個目標對象統一添加功能(如日志、事務管理)。

攔截處理:動態代理對象在調用原生對象的方法時,通過統一的入口(如?InvocationHandler.invoke())攔截方法調用,從而實現對參數、方法執行過程和返回結果的增強或修改

一、核心作用

  1. 統一擴展功能:批量對多個類添加相同的增強邏輯。

  2. 解耦代碼:將核心業務邏輯與輔助功能(如權限校驗)分離。

  3. 動態適配:運行時根據需求靈活生成代理對象。


二、使用場景

  1. 框架級功能擴展:如Spring AOP(日志、事務、權限)。

  2. RPC調用:隱藏網絡通信細節,讓遠程調用像本地調用。

  3. 接口監控:統計接口調用耗時、成功率等。

  4. Mock測試:動態生成測試用的代理對象。

1. ClassLoader loader

  • 作用
    動態代理生成的字節碼需要被類加載器加載到 JVM 中。使用目標對象的類加載器可確保代理類能訪問目標類及其接口。

  • 示例

// 目標對象
UserService target = new UserServiceImpl();
// 使用目標對象的類加載器
ClassLoader loader = target.getClass().getClassLoader();

2.Class<?>[] interfaces

  • 作用
    指定代理類需要實現的接口。代理對象會實現這些接口的所有方法,并將方法調用委托給?InvocationHandler

  • 示例

// 目標對象實現的接口
Class<?>[] interfaces = target.getClass().getInterfaces();

3.InvocationHandler h

  • 作用
    定義代理邏輯的核心處理器。所有通過代理對象調用的方法,都會觸發?invoke()?方法,開發者可在此插入增強邏輯。

  • 示例

InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 增強邏輯(如記錄日志)System.out.println("調用方法: " + method.getName());// 調用目標對象的方法return method.invoke(target, args);}
};

具體實現:(初始)

    @Testvoid test02() {// 原生對象MathCalculator target = new MathCalculatorImpl();int add1 = target.add(1, 3);System.out.println("原生對象");System.out.println(add1);//創建動態代理/*三個參數proxy:代理對象->相當于明星的代理人method:代理對象調用目標對象的方法args:方法調用傳遞的參數*/InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 調用目標對象的方法并返回System.out.println("動態代理InvocationHandler的invoke中定義的");System.out.println("[日志] 調用了方法: " + method.getName());System.out.println("[日志] 獲得參數: " + Arrays.toString(args));return method.invoke(target, args);}};/*三個參數ClassLoader:確保代理類的正確加載。Interfaces:定義代理類的行為范圍(基于接口)。InvocationHandler:實現動態代理的核心邏輯(AOP 的基礎)。*/MathCalculator proxy = (MathCalculator) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),invocationHandler);int add = proxy.add(1, 3);System.out.println(add);}

具體實現:(改進)

一、核心業務邏輯

1. 接口?MathCalculator
  • 作用:定義業務契約,明確四則運算的規范。

  • 關鍵點

    • 聲明了加減乘除四個方法。

    • 作為動態代理的接口約束(JDK動態代理必須基于接口)。

2. 實現類?MathCalculatorImpl
  • 作用:具體實現四則運算的業務邏輯。

  • 關鍵點

    • 使用?@Component?標注為 Spring 組件(若配合 Spring 使用)。

    • 實現了?MathCalculator?接口中的方法。

    • 是動態代理的目標對象(原生對象)。

接口

package org.example.spring02.MathMethod;public interface MathCalculator {//定義四則運算public int div(int a, int b);public int mul(int a, int b);public int sub(int a, int b);public int add(int a, int b);}

實現類

package org.example.spring02.MathMethod.MIm;import org.example.spring02.MathMethod.MathCalculator;
import org.springframework.stereotype.Component;@Component
public class MathCalculatorImpl implements MathCalculator {@Overridepublic int add(int a, int b) {return a + b;}@Overridepublic int sub(int a, int b) {return a - b;}@Overridepublic int mul(int a, int b) {return a * b;}@Overridepublic int div(int a, int b) {return a / b;}}

二、日志工具模塊

日志工具類?logUtils
  • 作用:提供靜態方法統一管理日志格式。

  • 核心方法

    • logStart():記錄方法開始執行的日志(含方法名和參數)。

    • logEnd():記錄方法正常結束的日志(含結果)。

    • logException():記錄方法執行異常的日志。

  • 設計亮點

    • 使用?Object... args?支持可變參數,適配不同方法的參數。

    • 通過?Arrays.toString(args)?格式化參數輸出。

package org.example.spring02.log;import java.util.Arrays;public class logUtils {public static void logStart(String name , Object... args){System.out.println("開始執行方法:"+name+",參數:"+ Arrays.toString(args));}public static void logEnd(String name , Object result){System.out.println("方法:"+name+",執行結果:"+result);}public static void logException(String name , Exception e){System.out.println("方法:"+name+",執行異常:"+e.getMessage());}public static void logEnd(){System.out.println("方法執行結束");}}

三、動態代理模塊

動態代理類?DynamicProxy
  • 作用:生成代理對象,為原生對象的方法調用添加日志增強邏輯。

package org.example.spring02.MathMethod.proxy.dynamic;import org.example.spring02.log.logUtils;import java.lang.reflect.Proxy;
import java.util.Arrays;public class DynamicProxy {public static  Object newProxyInstance(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),(proxy, method, args) -> {System.out.println("動態代理開始");System.out.println("[日志] 調用了方法: " + method.getName());logUtils.logStart(method.getName(), args);System.out.println("[日志] 獲得參數: " + Arrays.toString(args));return method.invoke(target, args);});}
}

四、測試驗證模塊

測試方法?test03()
  • 作用:驗證動態代理的日志增強功能是否生效。

    @Testvoid test03(){MathCalculator o = (MathCalculator) DynamicProxy.newProxyInstance(new MathCalculatorImpl());int add = o.add(1, 3);System.out.println(add);}

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

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

相關文章

Java Lambda表達式:現代編程的簡潔之道

引言 在Java 8中&#xff0c;Lambda表達式的引入標志著Java語言向函數式編程邁出了重要一步。Lambda不僅簡化了代碼結構&#xff0c;還提升了開發效率&#xff0c;使得Java能夠更靈活地應對現代編程需求。本文將深入探討Lambda表達式的核心概念、語法規則、應用場景及其對Java…

BGP分解實驗·21——BGP選路原則之本地優先級

當使用BGP路徑屬性——本地優先級&#xff0c;進行路由優選時&#xff0c;優選“本地優先級”數值較大的那個。&#xff08;eBGP之間更新不攜帶這個屬性&#xff09; 實驗拓撲如下&#xff1a; 在未實現本地優先級策略前&#xff0c;先在各個BGP之間配置完成基本連接。 R1的基…

【redis】應用場景:共享會話和手機驗證碼

文章目錄 共享會話實現思路 手機驗證碼實現思路偽代碼實現生成驗證碼驗證驗證碼 共享會話 實現思路 如果每個應用服務器&#xff0c;維護自己的會話數據&#xff0c;此時彼此之間胡共享&#xff0c;用戶請求訪問到不同的服務器上&#xff0c;就可能會出現一些不能正確處理的情…

通義萬相 2.1 + 藍耘算力,AI 視頻生成的夢幻組合

在這個科技日新月異的時代&#xff0c;人工智能不斷刷新著我們對世界的認知。一次偶然的機會&#xff0c;我借助北京藍耘科技股份有限公司提供的算力支持&#xff0c;踏上了使用通義萬相 2.1 進行 AI 視頻生成的奇妙之旅。 目錄 1.1初遇藍耘科技&#xff1a; 1.2通義萬相 2.1…

【Go萬字洗髓經】Golang內存模型與內存分配管理

本文目錄 1. 操作系統中的虛擬內存分頁與進程管理虛擬內存與內存隔離 2. Golang中的內存模型內存分配流程內存單元mspan線程緩存mcache中心緩存mcentral全局堆緩存mheapheapArena空閑頁索引pageAlloc 3. Go對象分配mallocgc函數tiny對象分配內存 4.結合GMP模型來看內存模型tiny…

33.HarmonyOS NEXT NumberBox 步進器高級技巧與性能優化

HarmonyOS NEXT NumberBox 步進器高級技巧與性能優化 一、高級交互設計 1. 組件聯動控制 // 與Slider雙向綁定 State value: number 50Slider({value: this.value,onChange: (v) > this.value v })NumberBox({value: this.value,onChange: (v) > this.value v })2. …

關于ModbusTCP/RTU協議轉Ethernet/IP(CIP)協議的方案

IGT-DSER智能網關模塊支持西門子、倍福(BECKHOFF)、羅克韋爾AB&#xff0c;以及三菱、歐姆龍等各種品牌的PLC之間通訊&#xff0c;支持Ethernet/IP(CIP)、Profinet(S7)&#xff0c;以及FINS、MC等工業自動化常用協議&#xff0c;同時也支持PLC與Modbus協議的工業機器人、智能儀…

通義萬相2.1 × 藍耘智算:AIGC 界的「黃金搭檔」如何重塑創作未來?

在人工智能生成內容&#xff08;AIGC&#xff09;領域&#xff0c;通義萬相2.1與藍耘智算的結合&#xff0c;正以技術協同效應重新定義創作的可能性。這一組合不僅突破了傳統創作工具的效率瓶頸&#xff0c;更通過算法與算力的深度融合&#xff0c;為影視、廣告、游戲、教育等領…

【FreeRTOS】FreeRTOS操作系統在嵌入式單片機上裸機移植

目錄 一 RTOS概述 二 FreeRTOS移植 三 FreeRTOS使用 四 附錄 一 RTOS概述 先了解一些基礎概念&#xff0c;以下內容摘自FreeRTOS官網&#xff08;FreeRTOS? - FreeRTOS?&#xff09;&#xff1a; 【1】RTOS基礎知識 實時操作系統 (RTOS) 是一種體積小巧、確定性強的計算機…

文件包含漏洞第一關

一、什么是文件包含漏洞 1.文件包含漏洞概述 和SQL注入等攻擊方式一樣&#xff0c;文件包含漏洞也是一種注入型漏洞&#xff0c;其本質就是輸入一段用戶能夠控制的腳本或者代碼&#xff0c;并讓服務端執行。 什么叫包含呢&#xff1f;以PHP為例&#xff0c;我們常常把可重復使…

瑞芯微RK3576(1)-硬件設計

過年期間&#xff0c;趁著放假時間做了一款3576的核心板 方案是2G DDR432G emmc 引出所有IO口 關于接口方面&#xff0c;考慮了一段時間&#xff0c;最終決定使用BTB的模式&#xff0c;主要是能夠出更多的IO&#xff0c;方便拆卸&#xff0c;最讓我擔心的是BTB的位置問題 為了…

Java 大視界 -- Java 大數據在智能醫療藥品研發數據分析與決策支持中的應用(126)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

JWT的學習

1、HTTP無狀態及解決方案 HTTP一種是無狀態的協議&#xff0c;每次請求都是一次獨立的請求&#xff0c;一次交互之后就是陌生人。 以CSDN為例&#xff0c;先登錄一次&#xff0c;然后瀏覽器退出&#xff0c;這個時候在進入CSDN&#xff0c;按理說服務器是不知道你已經登陸了&…

時序和延時

1、延遲模型的類型 verilog有三種類型的延遲模型&#xff1a;分布延遲 、 集總延遲 、 路徑延遲&#xff08;pin to pin&#xff09; 1.1、 分布延遲 分布延遲是在每個獨立元件的基礎上進行定義的。 module M(output wire out ,input wire a …

SpringBoot基礎Kafka示例

這里將生產者和消費者放在一個應用中 使用的Boot3.4.3 引入Kafka依賴 <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency>yml配置 spring:application:name: kafka-1#kafka…

API調試工具的無解困境:白名單、動態IP與平臺設計問題

引言 你是否曾經在開發中遇到過這樣的尷尬情形&#xff1a;你打開了平臺的API調試工具&#xff0c;準備一番操作&#xff0c;結果卻發現根本無法連接到平臺&#xff1f;別急&#xff0c;問題出在調試工具本身。今天我們要吐槽的就是那些神奇的開放平臺API調試工具&#xff0c;…

多方安全計算(MPC)電子拍賣系統

目錄 一、前言二、多方安全計算(MPC)與電子拍賣系統概述2.1 多方安全計算(MPC)的基本概念2.2 電子拍賣系統背景與需求三、MPC電子拍賣系統設計原理3.1 系統總體架構3.2 電子拍賣中的安全協議3.3 數學與算法證明四、數據加解密模塊設計五、GPU加速與系統性能優化六、GUI設計與系…

【Linux篇】初識Linux指令(上篇)

Linux命令世界&#xff1a;從新手到高手的必備指南 一 Linux發展與歷史1.1 Linux起源與發展1.2 Linux與Windows操作系統對比 二 Linux常用操作指令2.1 ls命令 - “List”&#xff08;列出文件)2.2 pwd指令- "打印當前工作目錄"2.3 cd指令 - “Change Directory”&…

編程視界:C++命名空間

目錄 命名空間 為什么要使用命名空間 什么是命名空間 命名空間的使用方式 關鍵點總結 命名空間的嵌套使用 匿名命名空間 跨模塊調用問題 命名空間可以多次定義 總結 首先從C的hello,world程序入手&#xff0c;來認識一下C語言 #include <iostream> using name…

Redux 和 MobX 高頻面試題

Redux 和 MobX 是 React 生態中的兩大狀態管理方案&#xff0c;在面試中常涉及 原理、使用方式、對比、最佳實踐 等方面。以下是 高頻面試題 詳細答案&#xff0c;助你輕松應對面試&#xff01;&#x1f680; &#x1f525; Redux 部分 1. Redux 是什么&#xff1f;為什么需要…