Spring基礎只是—AOP的概念介紹

来源:https://www.cnblogs.com/yuhushen/archive/2022/06/11/16367192.html
-Advertisement-
Play Games

Spring容器包含兩個重要的特性:面向切麵編程(AOP)和控制反轉(IOC)。面向切麵編程是面向對象(OOP)的一種補充,在面向對象編程的過程中編程針對的目標是一個個對象,而面向切麵編程中編程針對的目標是一個個切麵。切麵支持跨類型跨對象(如事務的切麵可以加在任何地方)進行模塊化。 前言 AOP是S ...


Spring容器包含兩個重要的特性:面向切麵編程(AOP)和控制反轉(IOC)。面向切麵編程是面向對象(OOP)的一種補充,在面向對象編程的過程中編程針對的目標是一個個對象,而面向切麵編程中編程針對的目標是一個個切麵。切麵支持跨類型跨對象(如事務的切麵可以加在任何地方)進行模塊化。

前言

AOP是Spring的關鍵特性之一,雖然Spring的IOC特性並不依賴於AOP(意味著你可以只使用Spring的IOC特性而不使用AOP特性),但是二者結合起來可以靈活的實現很多中間件解決方案。比如我們經常使用的事務(@Transaction),就是通過AOP方案實現的。本文重點介紹AOP編程中的一些術語,這些術語不僅僅局限於Spring,它適用於所有的AOP編程。

Spring AOP示例

  1. 切麵(Aspect):面向切麵編程可以跨類跨對象進行切麵編程,一個切麵就是對一類橫切關註點的模塊化。
  2. 切入點(JoinPoint):程式執行過程中的一個點,如方法調用、欄位訪問和異常拋出等。
  3. 增強(Advice):用於對切麵增強,包含前增強、後增強和環繞增強。大多數AOP框架會對切入點進行攔截,併在切入點維護一個攔截器鏈。
  4. 目標對象(TargetObject):包含一個或者多個切麵的對象。
  5. AOP代理(AOPProxy):通過Java動態代理或者CGLib增強得到的代理對象。
  6. 織入(Weaving):將切麵整合到完整的流執行流程。

Spring的AOP的功能和目標

Spring的AOP使用純Java語言實現(如AspectJ就不是Java語言),不需要任何額外的編譯流程,不需要修改類載入器,適用於任何Servlet容器和應用服務。Spring的AOP只支持方法攔截,不支持欄位攔截,如果用戶需要使用欄位攔截,可以考慮引入AspectJ等類似的框架。

Spring的AOP框架和其它的框架有些不同,Spring的Aop框架不僅僅是為了提供一個AOP功能,它更重要的功能是和Spring的IOC容器結合,提供一些企業應用服務的解決方案(如事務等),我們可以和定義一個普通Bean一樣的方式去定以一個切麵。Spring的AOP不支持非常細粒度的AOP,只支持對容器中的Bean進行AOP,如果需要更細粒度的AOP,可以考慮使用AspectJ。Spring容器的一個優秀的特性就是非侵入性,所以你可以靈活的選擇自己的AOP方案,不一定非要使用Spring的方案。

代理方式

Spring對實現介面的方法預設使用Java動態代理實現AOP攔截,對於非介面方法則會使用CGLIB位元組碼工具實現代理。

@AspectJ的支持

@AspectJ註解可以把一個普通的Java類聲明為切麵。@AspectJ註解是AspectJ5引入的註解,Spring雖然可以讀取AspectJ5的註解用於切麵元數據的配置,但是在運行的時候使用的仍然是Spring AOP進行代理,而沒有使用AspectJ的編譯器或者織入邏輯。我們會在後續討論如何在Spring中使用AspectJ的編譯器和織入邏輯。

啟用@AspectJ

Spring預設沒有啟用AspectJ,如果你需要Spring支持@AspectJ註解對應的切麵,可以通過在配置類上添加註解或者使用XML啟用配置(AspectJ所在的包是:aspectjweaver.jar)。

通過Java註解啟用AspectJ註解支持

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

通過XML配置啟用AspectJ註解

<aop:aspectj-autoproxy/>

定義一個切麵

當啟用AspectJ支持之後,開發者定義的任何Aspect切麵會自動地被檢測到,然後Spring AOP會對切麵進行攔截。下麵兩個例子展示瞭如何配置AspectJ切麵,你可以通過添加@Component註解把切麵Bean註冊到Spring容器中。

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <!-- configure properties of the aspect here -->
</bean>
package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

聲明一個切入點

切入點程式運行過程中我們感興趣的一個點,Spring的AOP框架只支持發現對Spring Bean方法上的切入點,因此你可以簡單的把切入點理解為SpringBean的方法。

切入點確定感興趣的連接點,從而使我們能夠控制何時運行通知。springaop只支持springbean的方法執行連接點,因此可以將切入點看作與springbean上方法的執行相匹配。切入點聲明由兩部分組成:一部分是由名稱和任何參數組成的簽名,另一部分是確定我們感興趣的方法執行的切入點表達式。在AOP的@AspectJ註釋樣式中,切入點簽名由常規方法定義提供,切入點表達式由@pointcut註釋指示(用作切入點簽名的方法必須具有void返回類型)。切入點由兩部分組成,一部分是用於區別不同切入點的標識(下麵例子中的private void anyOldTransfer() {})),另外一部分是確定我們感興趣的Bean方法的表達式(下麵例子中的@Pointcut("execution(* transfer(..))")), 下麵的例子展示了一個切入點的定義:

@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature

Spring匹配切入點的語法使用了AspectJ5中的表達式語法,我們可以參考AspectJ文檔相關的語法.

常見的切入點匹配表達

Spring支持下麵常見的AspectJ切麵定義語法:

  • execution:用於匹配方法的連接點。
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} 
  • within:用於匹配類型內的方法。
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} 
  • this:匹配當前AOP代理對象的執行方法
@target(org.springframework.transaction.annotation.Transactional)
  • target:target匹配目標對象的類型,即被代理對象的類型,例如A繼承了B介面,則使用target("B"),target("A")均可以匹配到A
//	當前AOP對象實現了 IPointcutService介面的任何方法
@Pointcut("target(cn.javass.spring.chapter6.service.IPointcutService)")
private void anyPublicOperation() {} 
  • args:用於限定切點方法的參數類型。
args(java.io.Serializable)
  • @target:被代理對象應該包含指定的註解。
@target(org.springframework.transaction.annotation.Transactional)
  • @args: 被代理對象的參數包含指定的註解。
@args(com.xyz.security.Classified)
  • @within: 被代理的對象應包含指定註解
@within(org.springframework.transaction.annotation.Transactional)
  • @annotation:切入點包含指定的註解。
 @annotation(org.springframework.transaction.annotation.Transactional)

我們可以通過“&&”和“||”對多個條件進行組合,AspectJ還有很多其它的表達式,但是Spring不支持除上述表達式以外的其它表達式。AspectJ其它表達式包含: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode等。

我們在使用Spring的代理方法之前,應該知道其代理原理。Java動態代理只能攔截public介面方法上的調用,CGLib只能攔截public、protected和defult方法。如果你需要更深層次的攔截,可以考慮使用底層的Aspectj。

切麵的增強

我們在上面的步驟定義好了一個切入點,我們現在就可以對這個切入點進行額外操作,這些額外操作被稱為增強,Spring支持四種增強方式:前增強、後增強、異常增強和環繞增強。Spring支持在增強方法的定義上直接定義切入點。

前增強BeforeAdvice

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }
}

後增強

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }
}

異常增強

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }
}

環繞增強

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }
}

代理機制

我們前面說過,Spring AOP通過動態代理和CGLIB實現AOP對象的代理。我們可以通過如下配置設置動態代理全部走CGLIB。

<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>

代理工廠的使用

Spring AOP實現代理的核心類是AspectJProxyFactory,我們可以使用這個類編程式生成代理對象:

// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy()

本文最先發佈至微信公眾號,版權所有,禁止轉載!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 分享嘉賓:羅景 58同城 高級架構師 編輯整理:洪鵬飛 內容來源:DataFun AI Talk《連接效率優化實踐》 出品社區:DataFun **導讀:**本次分享由以下幾個部分構成—— 58的業務背景 綜合排序框架 效率優化框架 基礎數據流程(數據) 策略優化路徑(演算法) 效率優化平臺(工程) ...
  • 1、簡介 註:本文代碼參考 我們將開發本系列第一個應用,並藉此學習一些Android基本概念以及構成應用的UI組件。 初學習,如果沒能全部理解,也不必擔心,後續還會涉及這些內容並有更加詳細的講解。 馬上要開發的應用名叫GeoQuiz,它能給出一道道地理知識問題。用戶點擊TRUE或FALSE 按鈕來回 ...
  • 原文地址:Jetpack架構組件學習(3)——Activity Results API使用 - Stars-One的雜貨小窩 技術與時俱進,頁面跳轉傳值一直使用的是startActivityForResult方法,如今有了新的API實現方式,學習並稍微總結下 😄 startActivityForR ...
  • 淺說一下吧 這個小項目由h5和css還有js和jq寫的 主題內容為個人音樂 博客等 首頁一級導航欄 以及側邊欄 整合部分圖標(側邊欄未添加收起操作 時間原因 會的朋友們可以自行添加一個動畫就可以 在main.js中) 中間音樂部分為作者和歌詞 點擊播放的按鈕可彈出對應的視頻mv 視頻mv均為酷狗音樂 ...
  • 瀏覽本篇文章前可以先看之前的前端網頁介紹和html常用標簽以便更容易理解 本文目錄: CSS相關查閱文檔:https://kohler.lanzouo.com/i0XFcz8svob 文檔截圖 CSS 技術介紹 CSS 是「層疊樣式表單」。是用於(增強)控制網頁樣式並允許將樣式信息與網頁內容分離的一 ...
  • 本文基於 Bilibili - 自由的加百利 前置條件: 需掌握函數的編寫、傳參、返回、調用 理解作用域、掌握定時器的用法 知道引用類型和基本數據類型的區別 知道函數也是引用類型 聽說過同步非同步的概念 瞭解類和對象的關係 匿名函數 來看一下一個函數的基本屬性: 匿名函數的自運行 我們可以將一個普通函 ...
  • 日常開發過程過程中。樹形結構運用的非常頻繁。 例如:公司組織結構、各種分類結構、分組結構等等。 SET FOREIGN_KEY_CHECKS = 0; CREATE TABLE IF NOT EXISTS `tbl_sapo_group` ( `id` int(10) unsigned NOT NU ...
  • 引子 把大象裝進冰箱需要3步:打開冰箱門,把大象裝入冰箱,關閉冰箱門。 擴展一下,我們考慮把動物裝進冰箱的場景。比如,把豬🐷裝進冰箱,把狗🐶裝進冰箱,等等。 怎麼利用面向對象的思想來進行程式設計呢? talk is cheap, show me the code. 把大象裝進冰箱的程式設計及實現 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...