Spring AOP:橫切關注點的優雅解決方案

目錄

概要

和面向對象編程的區別

優點

AOP的底層原理

JDK動態代理技術

AOP七大術語

切點表達式

AOP實現方式

Spring對AOP的實現包括以下3種方式:

在本篇文章中,我們主要講解前兩種方式。

基于AspectJ的AOP注解式開發

定義目標類以及目標方法

定義切面類

目標類和切面類都納入Spring bean管理

在切面類中添加通知

再通知上添加切點表達式

在Spring配置文件中啟用自動代理

通知類型

接下來,編寫程序來測試這幾個通知的執行順序

切面的先后順序

優先使用切點表達式

基于XML配置方式的AOP

編寫目標類

編寫切面類,并編寫通知

編寫Spring配置文件


概要

在軟件開發的旅程中,我們常常會遇到一些橫切關注點(cross - cutting concerns),這些關注點如同貫穿整個應用程序的絲線,涉及多個不同的模塊和功能。例如,日志記錄、事務管理、權限驗證等功能,它們并非屬于某個特定的業務模塊,卻又在許多業務方法中都需要被執行。?

在傳統的編程模式下,為了實現這些橫切關注點,開發者往往不得不將相關的代碼片段分散地嵌入到各個業務方法中。以日志記錄為例,可能在每個需要記錄日志的業務方法開頭和結尾都要添加打印日志的代碼,這不僅導致代碼的大量重復,還使得業務邏輯與這些輔助功能的代碼緊密糾纏在一起,嚴重影響了代碼的可讀性、可維護性和可擴展性。?

Spring AOP 應運而生,它提供了一種優雅而強大的解決方案,能夠將這些橫切關注點從業務邏輯中分離出來,以一種模塊化的方式進行集中管理和維護。通過定義切面(Aspect),Spring AOP 可以在不修改業務邏輯代碼的前提下,在特定的連接點(Join Point,如方法調用、異常拋出等)插入相應的通知(Advice,如前置通知、后置通知、環繞通知等),從而實現對橫切關注點的統一處理。


和面向對象編程的區別

維度面向對象編程面向切面編程
核心思想以 “對象” 為中心,將現實世界抽象為類、對象及對象間的交互。以 “切面” 為中心,將橫切關注點從業務邏輯中分離,實現模塊化管理。
基本單元類(Class)和對象(Object),通過封裝、繼承、多態構建程序結構。切面(Aspect),通過定義切點(Pointcut)和通知(Advice)織入橫切邏輯。
關注點類型聚焦于業務邏輯的縱向模塊化(如用戶管理、訂單處理等獨立業務模塊)。聚焦于橫切關注點的橫向抽取(如日志、權限、事務等貫穿多個模塊的功能)。

優點

  1. 代碼復用性強
  2. 代碼易維護
  3. 使開發者更專注于業務邏輯

AOP的底層原理

JDK動態代理技術

為接口創建代理類的字節碼文件,使用ClassLoader將字節碼文件加載到JVM,創建代理類實例對象,執行對象的目標方法。

AOP七大術語

  1. 連接點Joinpoint:指那些被攔截到的點(位置)。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點
  2. 切點Pointcut:指我們要對哪些Joinpoint進行攔截的定義,在程序執行流程中,真正織入切面的方法(一個切點對應多個連接點)
  3. 切面Aspect:切點+通知就是一個切面,需要自己編寫和配置
  4. 通知Advice:通知又叫增強,就是具體你要織入的代碼
  5. 織入Weaving:把通知應用到目標對象上的過程(指把增強應用到目標對象來創建新的代理對象的過程)
  6. 代理對象 Proxy:一個目標對象被織入通知后產生的新對象
  7. 目標對象 Target:被織入通知的對象

其中的通知(Aspect)包括:

  • 前置通知:befer
  • 后置通知:after-returning
  • 最終通知:after
  • 異常通知:throwing
  • 環繞通知:around
public class Cat {?// Cat類的run方法?public void run() {?System.out.println("Cat is running");?}?
}?
?
class Test {?public static void main(String[] args) {?Cat cat = new Cat();?cat.run();?}?
}

切點表達式

切點表達式用來定義通知(Advice)往哪些方法上切入,語法格式如下:?

execution([訪問控制權限修飾符] 返回值類型 [全限定類名]方法名(形式參數列表) [異常])
  • 訪問權限控制符:(可選項)沒寫就是4種權限都可以
  • 返回值類型:(必填項)若為“*”,表示返回值類型任意?
  • 全限定類名:(可選項)兩個點“..”表示當前包以及子包下的所有類,若省略,就表示所有的類
  • 方法名:(必填項)“*”表示所有方法,set * 表示所有的set方法
  • 形式參數列表:(必填項)
    • ():表示沒有參數的列表
    • (..) :參數類型和個數隨意的方法
    • (*): 只有一個參數的方法
    • (*, String): 第一個參數類型隨意,第二個參數是String的
  • 異常:(可選項)省略是表示任意異常

如:

execution(public * com.powernode.mall.service.*.delete*(..))
表示返回值類型任意,處于com.powernode.mall.service包下的所有類的所有參數任意的deleteXxx方法execution(* com.powernode.mall..*(..))
任意修飾符、返回值類型的,處于com.powernode.mall報下的所有方法execution(* *(..))
表示該項目的所有方法

AOP實現方式

Spring對AOP的實現包括以下3種方式:

  1. Spring框架結合AspectJ框架實現的AOP,基于注解方式。
  2. Spring框架結合AspectJ框架實現的AOP,基于XML方式。
  3. Spring框架自己實現的AOP,基于XML配置方式。

在本篇文章中,我們主要講解前兩種方式。

基于AspectJ的AOP注解式開發

定義目標類以及目標方法
// 目標類
public class OrderService {// 目標方法public void generate(){System.out.println("訂單已生成!");}
}
定義切面類
// 切面類
@Aspect
public class MyAspect {
}

注解@Aspect會告訴Spring該類是一個注解類

目標類和切面類都納入Spring bean管理

在目標類OrderService上添加@Component注解

在切面類MyAspect類上添加**@Component**注解

在切面類中添加通知

// 切面類
@Aspect
@Component
public class MyAspect {// 這就是需要增強的代碼(通知)public void advice(){System.out.println("我是一個通知");}
}

再通知上添加切點表達式

// 切面類
@Aspect
@Component
public class MyAspect {// 切點表達式@Before("execution(* com.xxx.spring6.service.OrderService.*(..))")// 這就是需要增強的代碼(通知)public void advice(){System.out.println("我是一個通知");}
}

其中,注解@Before表示前置通知(具體的通知類型在上面有講過,下面也會有方法中的通知注解)

在Spring配置文件中啟用自動代理

 <!--開啟組件掃描--><context:component-scan base-package="com.xxx.spring6.service"/><!--開啟自動代理--><aop:aspectj-autoproxy proxy-target-class="true"/>

<aop:aspectj-autoproxy ?proxy-target-class="true"/> 開啟自動代理之后,凡是帶有@Aspect注解的bean都會生成代理對象。

proxy-target-class="true" 表示采用cglib動態代理。

proxy-target-class="false" 表示采用jdk動態代理。默認值是false。即使寫成false,當沒有接口的時候,也會自動選擇cglib生成代理類(AOP的底層就是動態代理,關于動態代理的內容可查看代理機制) ??

通知類型

  • 前置通知:@Before 目標方法執行之前的通知
  • 后置通知:@AfterReturning 目標方法執行之后的通知
  • 環繞通知:@Around 目標方法之前添加通知,同時目標方法執行之后添加通知。
  • 異常通知:@AfterThrowing 發生異常之后執行的通知
  • 最終通知:@After 放在finally語句塊中的通知

接下來,編寫程序來測試這幾個通知的執行順序

// 切面類
@Component
@Aspect
public class MyAspect {@Around("execution(* com.xxx.spring6.service.OrderService.*(..))")public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("環繞通知開始");// 執行目標方法。proceedingJoinPoint.proceed();System.out.println("環繞通知結束");}@Before("execution(* com.xxx.spring6.service.OrderService.*(..))")public void beforeAdvice(){System.out.println("前置通知");}@AfterReturning("execution(* com.xxx.spring6.service.OrderService.*(..))")public void afterReturningAdvice(){System.out.println("后置通知");}@AfterThrowing("execution(* com.xxx.spring6.service.OrderService.*(..))")public void afterThrowingAdvice(){System.out.println("異常通知");}@After("execution(* com.xxx.spring6.service.OrderService.*(..))")public void afterAdvice(){System.out.println("最終通知");}}

讀者可自行測試,其中異常通知需要產生異常才能觸發,當發生異常之后,最終通知也會執行,因為最終通知@After會出現在finally語句塊中。出現異常之后,“后置通知”和“環繞通知”的結束部分不會執行

切面的先后順序

我們知道,業務流程當中不一定只有一個切面,可能有的切面控制事務,有的記錄日志,有的進行安全控制,如果多個切面的話,順序如何控制:可以使用@Order注解來標識切面類,為@Order注解的value指定一個整數型的數字,數字越小,優先級越高

優先使用切點表達式

  • 上面的切點表達式重復寫了多次,沒有得到復用,同時如果要修改切點表達式,需要修改多處,難維護
  • 我們可以將切點表達式單獨的定義出來,在需要的位置引入即可
// 切面類
@Component
@Aspect
@Order(2)
public class MyAspect {@Pointcut("execution(* com.xxx.spring6.service.OrderService.*(..))")public void pointcut(){}@Around("pointcut()")public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("環繞通知開始");// 執行目標方法。proceedingJoinPoint.proceed();System.out.println("環繞通知結束");}@Before("pointcut()")public void beforeAdvice(){System.out.println("前置通知");}@AfterReturning("pointcut()")public void afterReturningAdvice(){System.out.println("后置通知");}@AfterThrowing("pointcut()")public void afterThrowingAdvice(){System.out.println("異常通知");}@After("pointcut()")public void afterAdvice(){System.out.println("最終通知");}}

使用@Pointcut注解來定義獨立的切點表達式。

注意這個@Pointcut注解標注的方法隨意,只是起到一個能夠讓@Pointcut注解編寫的位置

基于XML配置方式的AOP

編寫目標類

// 目標類
public class VipService {public void add(){System.out.println("保存vip信息。");}
}

編寫切面類,并編寫通知

// 負責計時的切面類
public class TimerAspect {public void time(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {long begin = System.currentTimeMillis();//執行目標proceedingJoinPoint.proceed();long end = System.currentTimeMillis();System.out.println("耗時"+(end - begin)+"毫秒");}
}

編寫Spring配置文件

    <!--納入spring bean管理--><bean id="vipService" class="com.xxx.spring6.service.VipService"/><bean id="timerAspect" class="com.xxx.spring6.service.TimerAspect"/><!--aop配置--><aop:config><!--切點表達式--><aop:pointcut id="p" expression="execution(* com.xxx.spring6.service.VipService.*(..))"/><!--切面--><aop:aspect ref="timerAspect"><!--切面=通知 + 切點--><aop:around method="time" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>

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

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

相關文章

開源 Arkts 鴻蒙應用 開發(三)Arkts語言的介紹

文章的目的為了記錄使用Arkts 進行Harmony app 開發學習的經歷。本職為嵌入式軟件開發&#xff0c;公司安排開發app&#xff0c;臨時學習&#xff0c;完成app的開發。開發流程和要點有些記憶模糊&#xff0c;趕緊記錄&#xff0c;防止忘記。 相關鏈接&#xff1a; 開源 Arkts …

hot100 -- 16.多維動態規劃

1.不同路徑 問題&#xff1a; 一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為 “Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為 “Finish” &#xff09;。 問總共有多少條…

優先級繼承和優先級天花板(pthread_mutexattr_setprotocol)

優先級繼承和優先級天花板&#xff0c;均可以解決優先級翻轉問題。 優先級翻轉&#xff1a; 實例觀察優先級翻轉和優先級繼承現象-CSDN博客 如果有兩個線程A和B&#xff0c;A的優先級大于B的優先級。在B獲取鎖之后&#xff0c;釋放鎖之前&#xff0c;A想要獲取鎖&#xff0c…

Spark on Hive表結構變更

Spark on Hive表結構變更 1、表結構變更概述1、表結構變更概述 在Spark on Hive架構中,表結構(Schema)變更是一個常見且重要的操作。理解其背景、使用場景以及具體方式對于大數據平臺管理至關重要 1.1、Spark on Hive元數據管理 Hive Metastore(HMS): 核心組件。它是一個…

NotePad++ 怎么沒有找到插件管理?

今天想使用NotePad閱讀markdown文檔&#xff0c;卻發現未安裝插件&#xff0c;本想通過插件管理安裝一下&#xff0c;結果沒有插件管理&#xff01;&#xff01;&#xff01;&#xff01; 我發現幫助菜單里面有一個入口 可惜的是網頁無法打開。。。 只能自己下載插件了。 將插件…

內容搜索軟件AnyTXT.Searcher忘記文件名也能搜,全文檢索 1 秒定位文件

各位文件搜索小能手們&#xff01;你們有沒有過這種糟心事兒&#xff0c;想找個文件&#xff0c;死活想不起文件名&#xff0c;在電腦里一頓亂翻&#xff0c;眼睛都找瞎了也沒找到。今天我就給你們介紹一款神器——AnyTXT.Searcher&#xff0c;它可是免費的全文檢索工具&#x…

uniapp實現像qq消息列表左滑顯示右側操作欄效果

先看效果圖 代碼 SlidableChatEntry.vue <template><!-- 聊天項列表 --><view class"chat-item"touchstart"handleTouchStart($event)"touchmove"handleTouchMove($event)"touchend"handleTouchEnd()"><!-- 聊…

收集了一些用python做mysql增刪改查的資料

還是因為最近在開發fastapi應用、現在需要把一些關鍵信息存庫&#xff0c;所以就很想要一些這方面的資料。我這里找到一些&#xff0c;希望你看了帖子能節省一些時間。 前邊說過如何搭建fastapi開發環境&#xff0c;帖子鏈接為&#xff1a; https://blog.csdn.net/weixin_4298…

嵌入式軟件面經(一)Q: 什么是Modbus協議?它有哪些特點?

Modbus協議是一種開放式的工業通訊協議&#xff0c;最初由Modicon公司&#xff08;現施耐德電氣Schneider Electric&#xff09;于1979年開發&#xff0c;廣泛應用于工業現場設備之間的數據通訊&#xff0c;尤其適用于PLC&#xff08;可編程邏輯控制器&#xff09;與現場儀表、…

Java-52 深入淺出 Tomcat SSL工作原理 性能優化 參數配置 JVM優化

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持續更新中&#xff01;&#xff08;長期更新&#xff09; 目前2025年06月13日更新到&#xff1a; AI煉丹日志-29 - 字節…

day44-硬件學習之arm啟動代碼

一、跳轉指令實現函數調用 LR&#xff08;鏈接寄存器&#xff09;&#xff1a;保留函數返回地址 1.1 bl指令 跳轉指令bl,使LR保存當前函數進入前的下一條指令的地址&#xff0c;使函數調用后可以返回下一條指令的地址&#xff1b; 1.2 bx指令 跳到目標地址&#xff1b; 1.3 棧…

【數據結構】七種常見排序算法

&#x1f970;&#x1f970;&#x1f970;來都來了&#xff0c;不妨點個關注叭&#xff01; &#x1f449;博客主頁&#xff1a;歡迎各位大佬!&#x1f448; 歡迎來到排序算法的學習&#xff0c;恭喜你&#xff01;本期內容主要介紹排序算法&#xff0c;一起來探索吧~ &#xf…

Spring AOP 代理模式解析

一、核心概念&#xff1a;代理模式就像房屋中介 想象你要租一套房子&#xff1a; 你&#xff1a;租客&#xff08;業務調用者&#xff09;房東&#xff1a;房主&#xff08;真實業務對象&#xff09;中介&#xff1a;代理對象 傳統方式&#xff08;無代理&#xff09; 租客…

智能制造——案例解讀16頁制造業指標體系搭建指導【附全文閱讀】

文檔的主要內容可以總結如下&#xff1a; **文檔概述**&#xff1a; 本文檔詳細探討了企業為何需要指標體系、指標體系的定義、如何搭建指標體系、如何有效拆解和管理指標&#xff0c;并最后提供了制造業指標體系的參考。 **主要內容**&#xff1a; 1. **企業為什么需要指標體系…

Pandas 數據清洗

數據清洗是數據分析過程中至關重要的一環&#xff0c;也是初學者最容易忽視的步驟。本文將詳細介紹如何使用Pandas進行數據清洗&#xff0c;涵蓋空值處理、日期格式修正、錯誤數據識別和重復數據刪除四大核心內容。 1. Pandas 清洗空值 空值是數據集中最常見的問題之一&#…

C++容器之 forward_list (單向鏈表)使用說明

目錄 1. 語法格式 2. 說明 3. 用法示例 1. 語法格式 描述控制可變長度元素序列的對象。該序列存儲為單向(前向)鏈接的節點列表&#xff0c;每個節點包含一個 Type 類型的成員。 template <class Type, class Allocator allocator<Type>> class forward_lis…

ali 輕量服務器安裝nginx

# Ubuntu sudo apt install nginx-light # 精簡版 # CentOS sudo yum install nginx #啟動并設置開機自啟 sudo systemctl daemon-reload sudo systemctl start nginx sudo systemctl enable nginx #驗證安裝 nginx -v curl -I 127.0.0.1 #常用命令&#xff1a; # 重新加載配…

【設計模式】4.代理模式

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 代理模式 1. 第一版 class SchoolGirl:def __init__(self):self._name Nonepropertydef name(self):return self._namename.setterdef name(self, valu…

基于微信小程序的美食點餐訂餐系統

文章目錄 1. 項目概述2. 項目思維導圖3. 系統架構特點4. 核心模塊實現代碼1. 登錄注冊2. 首頁模塊實現4. 分類模塊實現5. 購物車模塊實現6. 訂單模塊實現 5. 注意事項6. 項目效果截圖7. 關于作者其它項目視頻教程介紹 1. 項目概述 在移動互聯網時代&#xff0c;餐飲行業數字化…

[neo4j]介紹4個開源的知識圖譜項目

項目主要介紹幾個開源項目&#xff1a; QASystemOnMedicalKG&#xff1a;醫療知識圖譜問答 https://github.com/liuhuanyong/QASystemOnMedicalKG Agriculture_KnowledgeGraph&#xff1a;農業知識圖譜 Financial-Knowledge-Graphs&#xff1a;小型金融知識圖譜 stock-know…