Spring系列四:AOP切面編程 第一部分

AOP切面編程

  • 💗AOP-官方文檔
    • 🍝AOP 講解
    • 🍝AOP APIs
  • 💗動態代理
    • 🍝初探動態代理
    • 🍝動態代理深入
    • 🍝AOP問題提出
      • 📗使用土方法解決
      • 📗 對土方法解耦-開發最簡單的AOP類
      • 📗 土方法缺點

上文中, 我們學習到了 Spring系列三:基于注解配置bean

接下來我們學習, AOP切面編程

Spring項目
在這里插入圖片描述

💗AOP-官方文檔

🍝AOP 講解


AOP 講解: spring-framework-5.3.8\docs\reference\html/core.html
在這里插入圖片描述

🍝AOP APIs


AOP APIs: spring-framework-5.3.8\docs\reference\html/core.html

在這里插入圖片描述
spring-framework-5.3.8/docs/javadoc-api/index.html

在這里插入圖片描述

💗動態代理

🍝初探動態代理

需求說明
1.由Vehicle (交通工具接口, 有一個run方法), 下面有兩個類 Car 和 Ship
2.當運行Car對象的 run 方法 和 ship對象的 run 方法時, 輸入如下內容

交通工具開始運行了…
輪船在海上航行…
交通工具停止運行了…

交通工具開始運行了…
小汽車在路上跑…
交通工具停止運行了…


解決方案一: 傳統方案

1.新建com.zzw.spring.aop.proxy.Vehicle接口

//接口, 該接口有run方法
public interface Vehicle {void run();
}

2.新建com.zzw.spring.aop.proxy.Car

public class Car implements Vehicle {@Overridepublic void run() {System.out.println("交通工具開始運行了....");System.out.println("小汽車在路上 running....");System.out.println("交通工具停止運行了....");}
}

3.新建com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {@Overridepublic void run() {System.out.println("交通工具開始運行了....");System.out.println("大輪船在路上 running....");System.out.println("交通工具停止運行了....");}
}

4.新建com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {@Testpublic void run() {//OOP基礎=>java基礎Vehicle vehicle = new Ship();//動態綁定vehicle.run();}
}

來思考一下, 這個解決方案好嗎? ====> 代碼冗余, 其實就是單個對象的調用, 并沒有很好的解決.


解決方案二: 動態代理方式
動態代理解決思路: 在調用方法時, 使用反射機制, 根據方法去決定調用哪個對象方法

1.新建com.zzw.spring.aop.proxy.VehicleProxyProvider

public class VehicleProxyProvider {//定義一個屬性//target_vehicle 表示真正要執行的對象//該對象實現了Vehicle接口private Vehicle target_vehicle;//構造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle = target_vehicle;}//編寫一個方法, 可以返回一個代理對象public Vehicle getProxy() {//得到類加載器ClassLoader classLoader =target_vehicle.getClass().getClassLoader();//得到要代理對象/被執行對象 的接口信息, 底層是通過接口來完成調用Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();//創建InvocationHandler 對象//因為 InvocationHandler 是接口, 所以我們可以通過匿名對象的方式來創建該對象/*** public interface InvocationHandler {*     public Object invoke(Object proxy, Method method, Object[] args)*          throws Throwable;* }* invoke 方法是將來執行target_vehicle的方法時, 會調用到*/InvocationHandler invocationHandler = new InvocationHandler() {/*class VehicleProxyProvider$01 implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具開始運行了....");//這里是我們的反射基礎 => OOPObject result = method.invoke(target_vehicle, args);System.out.println("交通工具停止運行了....");return result;}}InvocationHandler invocationHandler = new VehicleProxyProvider$01();*//*** invoke 方法是將來執行我們的target_vehicle的方法時, 會調用到** @param proxy  表示代理對象* @param method 就是通過代理對象調用方法時, 的那個方法 代理對象.run()* @param args   表示調用 代理對象.run(xx) 傳入的參數* @return 表示 代理對象.run(xx) 執行后的結果.* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具開始運行了....");//這里是我們的反射基礎 => OOP//method 是 public abstract void com.zzw.spring.aop.proxy.Vehicle.run()//target_vehicle 是 Ship對象//args 是 null//這里通過反射+動態綁定機制, 就會執行到被代理對象的方法//執行完畢就返回Object result = method.invoke(target_vehicle, args);System.out.println("交通工具停止運行了....");return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)解讀1.Proxy.newProxyInstance() 可以返回一個代理對象2.ClassLoader loader: 類加載器,3.Class<?>[] interfaces 就是將來要代理的對象的接口信息4.InvocationHandler h 調用處理器/對象, 有一個非常重要的方法invoke*/Vehicle proxy =(Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

2.修改com.zzw.spring.aop.proxy.Vehicle

public interface Vehicle {void run();public String fly(int height);
}

3.修改com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {@Overridepublic void run() {//System.out.println("交通工具開始運行了....");System.out.println("大輪船在路上 running....");//System.out.println("交通工具停止運行了....");}@Overridepublic String fly(int height) {return "輪船可以飛行 高度=" + height + "米";}
}

4.com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {@Testpublic void proxyRun() {//創建Ship對象Ship ship = new Ship();//創建VehicleProxyProvider對象, 并且我們要傳入代理的對象VehicleProxyProvider vehicleProxyProvider= new VehicleProxyProvider(ship);//獲取代理對象, 該對象可以代理執行方法//解讀//1.proxy 編譯類型Vehicle,//2.運行類型 是代理類型, 即 class com.sun.proxy.$Proxy8Vehicle proxy = vehicleProxyProvider.getProxy();System.out.println("proxy的編譯類型是 Vehicle");System.out.println("proxy的運行類型是" + proxy.getClass());//下面解讀/debug怎么執行到 代理對象的 public Object invoke(Object proxy, Method method, Object[] args)//梳理完畢, proxy的編譯類型是Vehicle, 運行類型是Proxy  class com.sun.proxy.$Proxy8//所以當執行run方法時, 會執行到 代理對象的invoke//如果體現動態 [1.被代理的對象 2.方法]//proxy.run();String result = proxy.fly(10000);System.out.println("result=" + result);System.out.println("ok");}

5.debug
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

🍝動態代理深入

需求說明
1.有一個SmartAnimal 接口, 可以完成簡單的加減法, 要求在執行 getSum()getSub() 時, 輸出執行前, 執行過程, 執行后的日志結果. 輸出內容如下:

日志-方法名-getSum-參數 1.5 4.5
方法內部打印result = 6.0
日志-方法名-getSum-結果result= 6.0
=================================
日志-方法名-getSub-參數 1.4 3.3
方法內部打印result = -1.9
日志-方法名-getSub-結果result= -1.9


解決方案一: 傳統方案

1.新建com.zzw.spring.aop.proxy2.SmartAnimalAble接口

public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}

2.com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {System.out.println("日志-方法名-getSum-參數 " + i + " " + j);float result = i + j;System.out.println("方法內部打印result = " + result);System.out.println("日志-方法名-getSum-結果result= " + (i + j));return result;}@Overridepublic float getSub(float i, float j) {System.out.println("日志-方法名-getSub-參數 " + i + " " + j);float result = i - j;System.out.println("方法內部打印result = " + result);System.out.println("日志-方法名-getSub-結果result= " + (i - j));return result;}
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {@Testpublic void run() {SmartAnimalAble smartAnimalAble = new SmartCat();smartAnimalAble.getSum(1.5f, 4.5f);System.out.println("=================================");smartAnimalAble.getSub(1.4f, 3.3f);}
}

解決方案二: 動態代理方式
考慮代理對象調用方法(底層是反射調用)時, 可能出現的異常- [橫切關注點]

1.新建com.zzw.spring.aop.proxy2.MyProxyProvider

//可以返回一個動態代理對象, 可以執行SmartCat對象的方法
public class MyProxyProvider {//這是一個屬性, 是我們要執行的目標對象//該對象實現了SmartAnimalAble接口private SmartAnimalAble target_obj;//構造器MyProxyProvider(SmartAnimalAble target_obj) {this.target_obj = target_obj;}//編寫一個方法, 可以返回一個代理對象//該代理對象可以執行目標方法public SmartAnimalAble getProxy() {//1.得到類加載器ClassLoader classLoader =target_obj.getClass().getClassLoader();//2.得到要執行的目標對象的接口信息Class<?>[] interfaces = target_obj.getClass().getInterfaces();//3.創建InvocationHandler 對象InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {System.out.println("方法執行前-日志-方法名-" + name + "-參數 "+ Arrays.asList(args));//這里從aop的角度看,就是一個橫切關注點-前置通知//使用反射調用方法result = method.invoke(target_obj, args);System.out.println("方法執行正常結束-日志-方法名-" + name + "-結果result= "+ result);//這里從aop的角度看, 也是一個橫切關注點-返回通知return result;} catch (Exception e) {e.printStackTrace();//如果反射執行方法時, 出現異常, 就會進入到catch{}System.out.println("方法執行異常-日志-方法名-" + name + "-異常類型="+ e.getClass().getName());//這里從aop的角度看, 又是一個橫切關注點-異常通知} finally {//不管你是否出現了異常, 最終都會執行到 finally {}//這里從aop的角度看, 還是一個橫切關注點-最終通知System.out.println("方法最終結束-日志-方法名-" + name);}return result;}};//創建代理對象SmartAnimalAble proxy =(SmartAnimalAble) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

2.修改com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {//System.out.println("日志-方法名-getSum-參數 " + i + " " + j);float result = i + j;System.out.println("方法內部打印result = " + result);//System.out.println("日志-方法名-getSum-結果result= " + (i + j));return result;}@Overridepublic float getSub(float i, float j) {//System.out.println("日志-方法名-getSub-參數 " + i + " " + j);float result = i - j;System.out.println("方法內部打印result = " + result);//System.out.println("日志-方法名-getSub-結果result= " + (i - j));return result;}
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {@Testpublic void smartCatTestProxy() {//創建SmartCat對象SmartAnimalAble smartAnimalAble = new SmartCat();MyProxyProvider myProxyProvider= new MyProxyProvider(smartAnimalAble);//獲取代理對象, 該對象可以代理執行方法SmartAnimalAble proxy = myProxyProvider.getProxy();System.out.println("proxy的編譯類型是 SmartAnimalAble");System.out.println("proxy的運行類型是 " + proxy.getClass());//proxy的編譯類型是SmartAnimalAble, 運行類型是 Class com.sun.proxy.$Proxy8//所以當執行getSum方法時, 會執行到 代理對象的invokeproxy.getSum(1.2f, 2.4f);System.out.println("=================================");proxy.getSub(1.3f, 4.5f);System.out.println("ok");}
}

🍝AOP問題提出

MyProxyProvider.java中, 我們的輸出語句功能比較弱, 在實際開發中, 我們希望是以一個方法的形式, 嵌入到真正執行的目標方法前.

如圖分析
在這里插入圖片描述

📗使用土方法解決

需求分析
使用土方法解決前面的問題, 后面使用Spring的AOP組件完成

1.先建一個包, 把相關文件拷貝過來, 進行修改完成. ----這里只是模擬, 并沒有真的新建包

//我們的一個方法, 在目標對象執行前執行
public void before(Method method, Object[] args) {System.out.println("before方法執行前-日志-方法名-" + method.getName() + "-參數 "+ Arrays.asList(args));//這里從aop的角度看,就是一個橫切關注點-前置通知
}//我們的一個方法, 在目標對象執行后執行
public void after(Method method, Object result) {System.out.println("after方法執行正常結束-日志-方法名-" + method.getName() + "-結果result= "+ result);//這里從aop的角度看, 也是一個橫切關注點-返回通知
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;before(method, args);//使用反射調用方法result = method.invoke(target_obj, args);after(method, result);return result;
} 

2.該方法問題分析: 耦合度高

📗 對土方法解耦-開發最簡單的AOP類

1.新建com.zzw.spring.aop.proxy2.ZzwAOP

public class ZzwAOP {//我們的一個方法, 在目標對象執行前執行public static void before(Method method, Object[] args) {System.out.println("ZzwHsp-方法執行前-日志-方法名-" + method.getName() + "-參數 "+ Arrays.asList(args));//這里從aop的角度看,就是一個橫切關注點-前置通知}//我們的一個方法, 在目標對象執行后執行public static void after(Method method, Object result) {System.out.println("ZzwHsp-方法執行正常結束-日志-方法名-" + method.getName() + "-結果result= "+ result);//這里從aop的角度看, 也是一個橫切關注點-返回通知}
}

2.修改com.zzw.spring.aop.proxy2.MyProxyProvider

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {//before(method, args);ZzwAOP.before(method, args);//使用反射調用方法result = method.invoke(target_obj, args);//after(method, result);ZzwAOP.after(method, result);return result;} catch (Exception e) {}
}

📗 土方法缺點

土方法 不夠靈活;
土方法 復用性差;
土方法 是一種硬編碼 (因為沒有注解和反射支撐)

Spring AOP 閃亮登場 - 底層是ASPECTJ

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

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

相關文章

【云計算】云計算中IaaS、PaaS、SaaS介紹

0 隨著云計算、大數據、人工智能發展迅速,布局“云”已經是互聯網企業共識。云計算的服務類型分為三種,分別為IaaS、PaaS、SaaS,這三個分別是什么意思,今天做一個簡單的介紹和了解。 一、云計算 云計算是用戶需求通過Internet獲取計算資源,把計算資源包裝成服務,提供給…

使用HYPRE庫并行裝配IJ稀疏矩陣指南: 矩陣預分配和重復利用

使用HYPRE庫并行裝配IJ稀疏矩陣指南 HYPRE是一個流行的并行求解器庫&#xff0c;特別適合大規模稀疏線性系統的求解。下面介紹如何并行裝配IJ格式的稀疏矩陣&#xff0c;包括預先分配矩陣空間和循環使用。 1. 初始化矩陣 首先需要創建并初始化一個IJ矩陣&#xff1a; #incl…

目標跟蹤最新文章閱讀列表

AAAI2025 TrackFormer: Multi-Object Tracking with Transformers 論文:https://arxiv.org/abs/2101.02702 代碼:https://github.com/timmeinhardt/trackformer AAAI2025 SUTrack 單目標跟蹤 論文:https://pan.baidu.com/s/10cR4tQt3lSH5V2RNf7-3gg?pwd=pks2 代碼:htt…

分布式GPU上計算長向量模的方法

分布式GPU上計算長向量模的方法 當向量分布在多個GPU卡上時&#xff0c;計算向量模(2-范數)需要以下步驟&#xff1a; 在每個GPU上計算本地數據的平方和跨GPU通信匯總所有平方和在根GPU上計算總和的平方根 實現方法 下面是一個完整的CUDA示例代碼&#xff0c;使用NCCL進行多…

高并發下單庫存扣減異常?飛算 JavaAI 自動化生成分布式事務解決方案

在電商、旅游等行業業務量激增&#xff0c;高并發下單場景中&#xff0c;傳統庫存扣減方式弊端盡顯。超賣問題因缺乏有效并發控制機制頻發&#xff0c;多個訂單同時訪問庫存數據&#xff0c;導致同一商品多次售出&#xff0c;訂單無法履約引發客戶投訴&#xff1b;同時&#xf…

MVCWebAPI使用FromBody接受對象的方法

近期在做軟件升級操作的時候突然想著需要的參數比較多&#xff0c;如果需要參數的話參數比較多&#xff0c;所有想著使用frombody來集合數據統一操作做了個樣張希望對您有幫助 代碼如下&#xff1a; /// <summary>/// 入口當前文件接口下的操作數據/// </summary>/…

Atlas 800I A2 離線部署 DeepSeek-R1-Distill-Llama-70B

一、環境信息 1.1、硬件信息 Atlas 800I A2 1.2、環境信息 注意&#xff1a;這里驅動固件最好用商業版&#xff0c;我這里用的社區版有點小問題 操作系統&#xff1a;openEuler 22.03 LTS NPU驅動&#xff1a;Ascend-hdk-910b-npu-driver_24.1.rc3_linux-aarch64.run NPU固…

NLP預處理:如何 處理表情符號

一、說明 本系列文總結了在NLP處理中&#xff0c;進行文本預處理的一些內容、步驟、處理工具包應用。本篇專門談論網上文章表情符號處理&#xff0c;對于初學者具有深刻學習和實驗指導意義。 二、介紹 表情符號已成為現代交流不可或缺的一部分&#xff0c;尤其是在社交媒體、…

C++/SDL 進階游戲開發 —— 雙人塔防(代號:村莊保衛戰 14)

&#x1f381;個人主頁&#xff1a;工藤新一 &#x1f50d;系列專欄&#xff1a;C面向對象&#xff08;類和對象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;終會照亮我前方的路 &#x1f389;歡迎大家點贊&#x1f44d;評論&#x1f4dd;收藏?文章 文章目錄 二…

解鎖空間數據新質生產力暨:AI(DeepSeek、ChatGPT)、Python、ArcGIS Pro多技術融合下的空間數據分析、建模與科研繪圖及論文寫作

人工智能&#xff08;AI&#xff09;與ArcGIS Pro的結合&#xff0c;為空間數據處理和分析開辟了前所未有的創新路徑。AI通過強大的數據挖掘、深度學習及自動化能力&#xff0c;可高效處理海量、多源、異構的空間數據&#xff0c;極大提升了分析效率與決策支持能力。而ArcGIS P…

18.2.go語言redis中使用lua腳本

在 Redis 中使用 Lua 腳本可以實現原子性操作、減少網絡開銷以及提高執行效率。 Redis 執行 Lua 腳本的原理 Redis 內置了 Lua 解釋器&#xff0c;能夠直接在服務器端執行 Lua 腳本。當執行 Lua 腳本時&#xff0c;Redis 會將腳本作為一個整體執行&#xff0c;保證腳本執行期…

?Unity_Demolition Media Hap (播放Hap格式視頻 超16K大分辨率視頻 流暢播放以及幀同步解決方案)

播放大分辨率視頻以及實現局域網視頻同步是許多開發者會遇到的需求,AVPro有一個 Ultra Edition版本,也能播放Hap格式視頻,之外就是Demolition Media Hap插件啦,實測即使是 7208*3808 大分辨率的視頻幀率還是能穩定在30幀,它能幫助我們輕松解決這些問題??。 一、插件概述 …

AI大模型知識與醫療項目實踐 - Java架構師面試實戰

AI大模型知識與醫療項目實踐 - Java架構師面試實戰 本文模擬了一場互聯網大廠的Java架構師面試&#xff0c;圍繞AI大模型知識、工具以及其在醫療項目中的實踐和趨勢展開討論。 第一輪提問 面試官&#xff1a; 馬架構&#xff0c;請您介紹一下AI大模型的基本概念及其在醫療領…

Windows 的文件系統不區分大小寫,Linux區分

在 Windows 系統中&#xff0c;文件系統默認是不區分大小寫的。這意味著在 Windows 上&#xff0c;文件名 ui_BalanceMeasureScreenUI.h 和 ui_balancemeasurescreenui.h 被視為同一個文件。因此&#xff0c;即使你在代碼中使用了不同的大小寫方式來引用同一個文件&#xff0c;…

Unity 資源合理性檢測

一&#xff1a;表格過度配置&#xff0c;表格資源是否在工程中存在&#xff0c;并輸出不存在的資源 import pandas as pd import glob import osassets [] count 0# 遍歷configs文件夾下所有xlsx文件 for file_path in glob.glob(configs/*.xlsx):count 1try:sheets pd.re…

Python爬蟲實戰:獲取高考資源網各學科精品復習資料

一、引言 高考資源網擁有豐富的高考復習資料,對于我們而言,獲取這些資源并整理分享能為考生提供有價值的幫助。然而,手動從網站查找和下載資源效率低且易出錯。利用 Python 爬蟲技術可實現自動化資源獲取,提高工作效率。但在爬取過程中,需考慮網站反爬機制,采取相應措施…

DuckDB:現代數據分析的“SQLite“內核革命

在數據工程、數據科學快速演進的今天&#xff0c;一個新的名字正在快速躥紅&#xff1a;DuckDB。 有人稱它是數據分析領域的SQLite&#xff0c;也有人稱它為下一代輕量級OLAP引擎。 無論哪種稱呼&#xff0c;都離不開一個事實&#xff1a; DuckDB 重新定義了小型數據倉庫和本地…

GIS開發筆記(16)解決基于osg和osgearth三維地圖上添加placeNode圖標點擊不易拾取的問題

一、實現效果 二、實現原理 在圖標添加的位置同時添加一個紅色圓球,半徑為5000~8000米,圖標和圓球掛接到同一個group節點,group節點再掛接到根節點,當點擊到圓球時,通過遍歷父節點就可以找到被點擊的圖標節點。 三、參考代碼 //添加圖標代碼 #pragma once #include &…

計算機網絡學習筆記 1-3章

第 1 章 計算機網絡體系結構 【考綱內容】 &#xff08;一&#xff09;計算機網絡概述 計算機網絡的概念、組成與功能&#xff1b;計算機網絡的分類&#xff1b; 計算機網絡的性能指標 &#xff08;二&#xff09;計算機網絡體系結構與參考模型 計算機網絡分層結構&#xff…

基于NVIDIA RTX 4090的COLMAP 3.7安裝指南:Ubuntu 20.04 + CUDA 11.8環境配置【2025最新版!!】

一、引言 三維重建技術作為計算機視覺領域的核心方向&#xff0c;在數字孿生、自動駕駛等領域具有重要應用價值。COLMAP作為開源的SfM&#xff08;Structure-from-Motion&#xff09;工具&#xff0c;其GPU加速特性可顯著提升重建效率。由于最新研究三維重建的需要&#xff08…