日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

輕松上手Spring AOP,掌握切面編程的核心技巧

來源: 責編: 時間:2024-04-11 09:06:07 164觀看
導讀Spring框架是我們使用比較多的一個框架,而AOP又是Spring的核心特性之一,本篇文章將介紹一下AOP的切點表達式、通知等特性及如何使用Spring AOP。AOP 是什么AOP(Aspect-Oriented Programming,面向切面編程) 是一種編程范式,

Spring框架是我們使用比較多的一個框架,而AOP又是Spring的核心特性之一,本篇文章將介紹一下AOP的切點表達式、通知等特性及如何使用Spring AOP。IPH28資訊網——每日最新資訊28at.com

AOP 是什么

AOP(Aspect-Oriented Programming,面向切面編程) 是一種編程范式,旨在將橫切關注點與核心業務邏輯相分離,以提高代碼的模塊化性、可維護性和復用性。IPH28資訊網——每日最新資訊28at.com

在傳統的面向對象編程中,程序的功能被模塊化為類和方法,但某些功能可能會跨越多個類和方法,如日志記錄、事務管理、安全控制等,這些功能不屬于核心業務邏輯,但又必須在多個地方重復使用,導致代碼重復和耦合性增加。IPH28資訊網——每日最新資訊28at.com

AOP提供了一種機制,可以將這些橫切關注點單獨定義,并在需要的地方插入到應用程序中,而不必修改核心業務邏輯。IPH28資訊網——每日最新資訊28at.com

AspectJ

AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP(面向切面編程) 語法,并擁有一個專門的編譯器,用于生成遵守Java字節編碼規范的Class文件。IPH28資訊網——每日最新資訊28at.com

AspectJ可以單獨使用,也可以整合到其他框架中。當單獨使用AspectJ時,需要使用專門的編譯器ajc。AspectJ屬于靜態織入,通過修改代碼來實現,包括編譯期織入等多種織入時機。IPH28資訊網——每日最新資訊28at.com

Spring集成AspectJ,可以在Spring中方便的使用AOP。IPH28資訊網——每日最新資訊28at.com

Spring AOP

Spring AOP核心概念主要包括以下幾個方面:IPH28資訊網——每日最新資訊28at.com

  1. 切面(Aspect):切面是模塊化橫切關注點的機制,由切入點和通知組成。在Spring AOP中,一個切面可以定義在什么時候、什么地方以及如何應用某種特定的行為到目標對象上。
  2. 連接點(Joinpoint):連接點是程序執行過程中的一個點,例如方法的調用、字段的訪問等。在Spring AOP中,一個連接點總是代表一個方法的執行。連接點是AOP框架可以在其上 “織入” 切面的點。
  3. 通知(Advice):通知定義了在切入點執行時要執行的代碼,它是增強應用到連接點上的行為。通知有多種類型,包括前置通知(Before Advice)、后置通知(After Advice) 、環繞通知(Around Advice) 、異常通知(After Throwing Advice) 和  返回通知(After Returning Advice)  。這些通知類型決定了增強在連接點上的執行順序和方式。
  4. 切點(Pointcut):切點用于定義通知應該應用到哪些連接點上。它是一組連接點的集合,這些連接點共享相同的特性。切點表達式用于匹配連接點,從而確定哪些連接點應該接收通知。
  5. 目標對象(Target Object) :被一個或多個切面所通知的對象。也被稱為被通知(advised)對象。由于Spring AOP是通過代理模式實現的,因此在運行時,目標對象總是被代理對象所包裹。
  6. 織入(Weaving):織入是將切面應用到目標對象并創建代理對象的過程。這是AOP框架在運行時或編譯時完成的核心任務。
  7. AOP代理(AOP Proxy):AOP框架創建的對象,用于實現切面編程。在Spring中,AOP代理可以是JDK動態代理或CGLIB代理。
  8. 引入(Introduction):用于向現有的類添加新的接口和實現,而不需要修改原始類的代碼。Introduction允許在不修改現有類結構的情況下,向類引入新的功能和行為。在 AspectJ 社區中,引入稱為類型間聲明(inter-type declaration)。

這些核心概念共同構成了AOP的基礎,使得我們能夠模塊化地處理橫切關注點,從而提高代碼的可維護性和可重用性。IPH28資訊網——每日最新資訊28at.com

切點表達式

Pointcut 表達式 是用來定義切入點的規則,它決定了哪些連接點(方法調用或方法執行)將會被通知所影響。在 Spring AOP 中,Pointcut 表達式通常由以下幾種規則和通配符組成:IPH28資訊網——每日最新資訊28at.com

  1. execution(): 用于匹配方法執行的連接點,它是最常用的切點指示器。它基于方法簽名進行匹配,可以指定方法的返回類型、包名、類名、方法名以及參數列表等。比如: @Pointcut("execution(* com.example.myapp.service.*.*(..))") 表示匹配com.example.myapp.service包下所有類的所有方法執行。
  2. within(): 匹配指定類型內的方法執行連接點。它通常用于匹配特定包或類中的所有方法。示例:@Pointcut("within(com.example.myapp.service.*)") 表示表示匹配com.example.myapp.service包下所有類的所有方法的執行。
  3. this(): 匹配當前代理對象為指定類型的連接點。這用于限制切點只匹配特定類型的代理對象。示例:@Pointcut("this(com.example.myapp.service.MyService)") 表示匹配當前代理對象類型為com.example.myapp.service.MyService的所有方法的執行。
  4. target(): 匹配目標對象為制定類型的連接點。與this()不同,target()是基于目標對象類型,而不是代理類型。示例:@Pointcut("target(com.example.myapp.service.MyServiceImpl)") 表示匹配目標對象類型為com.example.myapp.service.MyServiceImpl的所有方法的執行。
  5. args(): 匹配方法執行時參數為特定類型的連接點。示例:@Pointcut("args(java.io.Serializable)") 表示匹配方法執行時至少有一個參數是java.io.Serializable類型的連接點。
  6. @annotation(): 匹配執行的方法上帶有指定注解的連接點。示例:@Pointcut("@annotation(com.example.myapp.annotation.MyAnnotation)") 表示匹配執行的方法上帶有com.example.myapp.annotation.MyAnnotation注解的連接點。
  7. @target:用于匹配所有帶有特定注解的類或接口。 這個指示器通常與execution表達式結合使用,以進一步細化匹配條件。示例:@Pointcut("@target(com.example.annotation.MyAnnotation)") 表示匹配目標對象類型上帶有com.example.myapp.annotation.MyAnnotation注解的方法執行。
  8. @within:匹配指定類型帶有指定注解的連接點。與within()類似,但它是基于注解而不是包或類。示例: @Pointcut("@within(com.example.myapp.annotation.MyAnnotation)") 表示匹配帶有MyAnnotation注解的類的方法執行。
  9. bean():匹配Spring容器中特定名稱的bean的方法的執行。示例: @Pointcut("bean(myServiceImpl)") 表示匹配Spring容器中名稱為myServiceImplbean的方法的執行。
  10. @args():用于限制匹配的方法的參數必須有指定的注解。

帶有 @ 符的切點表達式都是需要指定注解的連接點。IPH28資訊網——每日最新資訊28at.com

這些規則可以通過邏輯運算符(如 &&、||、! )進行組合,以實現更復雜的 Pointcut 匹配規則。我們可以根據自己的需求,靈活地使用這些規則來定義切入點表達式,實現對目標方法的精確匹配和監控。IPH28資訊網——每日最新資訊28at.com

execution()

execution() 表達式使用的比較多,最復雜的一個表達式,這里重點介紹一下。IPH28資訊網——每日最新資訊28at.com

語法結構

execution() 表達式的語法結構如下:IPH28資訊網——每日最新資訊28at.com

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

其中,各部分的含義如下:IPH28資訊網——每日最新資訊28at.com

  • modifiers-pattern: 方法的訪問修飾符,如 public、protected 等,可以省略。
  • ret-type-pattern: 方法的返回類型,如 void、int 等。
  • declaring-type-pattern: 方法所屬的類的類型模式,可以使用通配符 * 匹配任意字符。
  • name-pattern: 方法的名稱模式,可以使用通配符 * 匹配任意字符。
  • param-pattern: 方法的參數模式,包括參數類型和個數。
  • throws-pattern: 方法拋出的異常類型。

示例

  • 所有公共方法的執行
execution(public * *(..))
  • 名稱以 set 開頭的所有方法的執行
execution(* set*(..))
  • AccountService 接口定義的任何方法的執行
execution(* com.xyz.service.AccountService.*(..))
  • service 包中定義的任何方法的執行
execution(* com.xyz.service.*.*(..))
  • service 包或其子包之一中定義的任何方法的執行
execution(* com.xyz.service..*.*(..))
  • 執行指定類型參數的方法
execution(* com.example.service.MyService.myMethod(String, int))

注意事項

  • 在 execution() 表達式中,通配符 * 可以用來匹配任意字符或任意個數的字符。
  • 使用 execution() 表達式時,需要注意合理地組織表達式,以確保精準地匹配目標方法。
  • 可以通過組合多個條件來更加靈活地定義切點,例如同時匹配方法的訪問修飾符、返回類型、類名、方法名等。

總的來說,execution() 方法提供了一種靈活且強大的方式來定義切點表達式,從而精確定位需要添加通知的目標方法。IPH28資訊網——每日最新資訊28at.com

通知(Advice)類型

圖片圖片IPH28資訊網——每日最新資訊28at.com

在 Spring AOP 中,通知(Advice)是在切入點(Pointcut)上執行的代碼。Spring 提供了幾種類型的通知,每種類型都對應著在連接點執行前、執行后或拋出異常時執行的不同代碼邏輯。這些通知對應著不同的注解,常用的通知注解包括:IPH28資訊網——每日最新資訊28at.com

  1. @Before: 在方法執行之前執行的通知。它有以下屬性:

value:要綁定的切點或者切點表達式。IPH28資訊網——每日最新資訊28at.com

argNames: 用于指定連接點表達式中方法參數的名稱,以便在通知方法中通過參數名來獲取方法參數的值。這樣可以在前置通知中訪問和處理方法參數的具體數值。該屬性即使不指定也能獲取參數。IPH28資訊網——每日最新資訊28at.com

  1. @AfterReturning: 在方法執行成功返回結果后執行的通知。它比 @Before注解多了2個屬性:

pointcut:作用和value屬性一樣,當指定pointcut時,會覆蓋value屬性的值。IPH28資訊網——每日最新資訊28at.com

returning:方法返回的結果將被綁定到此參數名,可以在通知中訪問方法的返回值。IPH28資訊網——每日最新資訊28at.com

  1. IPH28資訊網——每日最新資訊28at.com

    @AfterThrowing: 在方法拋出異常后執行的通知。它的屬性前3個和 @AfterReturning注解一樣,多了1個屬性:IPH28資訊網——每日最新資訊28at.com

    IPH28資訊網——每日最新資訊28at.com

throwing:指定方法拋出的異常將被綁定到此參數名,可以在通知中訪問方法拋出的異常。IPH28資訊網——每日最新資訊28at.com

  1. IPH28資訊網——每日最新資訊28at.com

    @After: 在方法執行后(無論成功或失敗)執行的通知。屬性同 @Before 注解。IPH28資訊網——每日最新資訊28at.com

    IPH28資訊網——每日最新資訊28at.com

  2. IPH28資訊網——每日最新資訊28at.com

    @Around: 環繞通知,能夠在方法執行前后都可以進行操作,具有最大的靈活性。屬性同  @Before 注解。IPH28資訊網——每日最新資訊28at.com

    IPH28資訊網——每日最新資訊28at.com

通知的執行順序為: @Around  ->  @Before ->  @AfterReturning(不拋異常情況) 或者  @AfterThrowing(拋異常情況)  -> @AfterIPH28資訊網——每日最新資訊28at.com

這些通知注解可以與 Pointcut 表達式結合使用,實現對目標方法的攔截和處理。通過選擇合適的通知類型,開發者可以根據需求在不同的時間點插入自定義的邏輯,實現對方法調用的控制和增強。IPH28資訊網——每日最新資訊28at.com

如何使用

講了那么多概念性的東西,下面來看怎么使用Spring AOP。IPH28資訊網——每日最新資訊28at.com

在Spring 中使用AOP也很簡單,主要分3步:IPH28資訊網——每日最新資訊28at.com

  1. 定義切面
  2. 定義切點
  3. 在具體通知上使用切點

準備階段

我這里使用的是Springboot 3.1.5、jdk 17,如果是Springboot低版本的可能需要引入 spring-boot-starter-aop 依賴,高版本的AOP已經包含在spring-boot-starter-web依賴中了:IPH28資訊網——每日最新資訊28at.com

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency>

Spring官網中介紹,使用Spring AOP要在啟動類或者配置類中加上 @EnableAspectJAutoProxy 注解開啟 AspectJ 注解的支持,在我使用的這個版本中并不需要,如果你的項目中切面未生效可以嘗試使用該注解。IPH28資訊網——每日最新資訊28at.com

定義一個接口,下面用于對這個接口及其實現類進行攔截:IPH28資訊網——每日最新資訊28at.com

public interface AopService {    /**     * 兩數除法     * @param a     * @param b     * @return     */    BigDecimal divide(BigDecimal a, BigDecimal b);    /**     * 兩數加法     * @param a     * @param b     * @return     */    BigDecimal add(BigDecimal a, BigDecimal b);}
@Servicepublic class MyAopServiceImpl implements AopService{    /**     * 兩數除法     *     * @param a     * @param b     * @return     */    @Override    public BigDecimal divide(BigDecimal a, BigDecimal b) {        return a.divide(b , RoundingMode.UP);    }    /**     * 兩數加法     *     * @param a     * @param b     * @return     */    @Override    public BigDecimal add(BigDecimal a, BigDecimal b) {        return a.add(b);    }}

定義切面

新建一個類,在類上加上@Aspect 注解,標記該類為切面。IPH28資訊網——每日最新資訊28at.com

@Component@Aspectpublic class AspectComponent {}

定義并使用切點

在切面中使用@Pointcut注解定義切點表達式,然后在通知注解中使用定義好的切點。在該示例中主要對AopService#divide()方法進行攔截。IPH28資訊網——每日最新資訊28at.com

@Component@Aspectpublic class AspectComponent { /**     * 匹配AopService接口的divide方法     */    @Pointcut("execution(* site.suncodernote.aop.AopService.divide(..))")    void dividePointCut(){    } /**     * 匹配AopService接口的divide方法     */    @Pointcut("within(site.suncodernote.aop.AopService+)")    void withinPointCut(){    } /**     * 匹配AopService接口的add方法 或者 divide方法     */    @Pointcut("execution(* site.suncodernote.aop.AopService.add(..)) || execution(* site.suncodernote.aop.AopService.divide(..))")    void addOrDividePointCut(){    } @Before("dividePointCut()")    public void beforeDivide(JoinPoint joinPoint){        System.out.println("---------------------@Before----------------");        printJoinPoint(joinPoint);    }    @After("dividePointCut()")    public void afterDivide(JoinPoint joinPoint){        System.out.println("---------------------@After----------------");        printJoinPoint(joinPoint);    }    @AfterReturning(pointcut = "dividePointCut()" , returning = "result")    public void afterReturningDivide(JoinPoint joinPoint , BigDecimal result){        System.out.println("---------------------@AfterReturning----------------");        System.out.println("返回結果="+result);        printJoinPoint(joinPoint);    }    @AfterThrowing(pointcut = "dividePointCut()" , throwing = "e")    public void afterThrowingDivide(JoinPoint joinPoint ,Exception e){        System.out.println("---------------------@AfterThrowing----------------");        System.out.println("異常:"+e.getMessage());        printJoinPoint(joinPoint);    }    @Around("dividePointCut()")    public Object aroundDivide(ProceedingJoinPoint joinPoint) throws Throwable {        System.out.println("---------------------@Around----------------");        printJoinPoint(joinPoint);        Object[] args = joinPoint.getArgs();        Object result = null;        try {            //執行方法            result = joinPoint.proceed(args);        } catch (Throwable throwable) {            throwable.printStackTrace();        }        System.out.println("返回值:"+result);        return result;    }    private void printJoinPoint(JoinPoint joinPoint){        Object[] args = joinPoint.getArgs();        Signature signature = joinPoint.getSignature();        System.out.println("方法名:"+signature.getName());        System.out.println("方法參數:"+ Arrays.toString(args));        System.out.println();    }}

測試

寫個簡單的單元測試,調用AopService#divide()方法,然后看一下輸出結果。IPH28資訊網——每日最新資訊28at.com

@SpringBootTestpublic class AOPTest {    @Resource    private AopService aopService;    @Test    public void testAOP() {        BigDecimal a = new BigDecimal(1);        BigDecimal b = new BigDecimal(2);//        aopService.add(a, b);        aopService.divide(a, b);    }}

測試結果:IPH28資訊網——每日最新資訊28at.com

---------------------@Around----------------方法名:divide方法參數:[1, 2]---------------------@Before----------------方法名:divide方法參數:[1, 2]---------------------@AfterReturning----------------返回結果=1方法名:divide方法參數:[1, 2]---------------------@After----------------方法名:divide方法參數:[1, 2]返回值:1

從測試結果中通知執行的順序是按照我們上面所說的執行順序執行的。IPH28資訊網——每日最新資訊28at.com

總結

本文介紹了Spring AOP的常用的切點表達式、通知注解等,我們可以利用AOP對業務邏輯的各個部分進行隔離,使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高開發的效率。IPH28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-82760-0.html輕松上手Spring AOP,掌握切面編程的核心技巧

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python新手必讀:掌握Bytearray對象的使用技巧

下一篇: Vue 3高級響應式數據探秘:原理、用法詳解與實戰示例!

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 鹤岗市| 象山县| 淳化县| 吉木乃县| 炎陵县| 定日县| 车险| 汝阳县| 武山县| 哈巴河县| 焦作市| 丰城市| 离岛区| 颍上县| 十堰市| 西平县| 光山县| 门头沟区| 甘谷县| 海丰县| 庆安县| 安仁县| 库尔勒市| 修武县| 浦城县| 忻州市| 平顺县| 曲阜市| 鄂州市| 梅州市| 全州县| 永嘉县| 西林县| 兴城市| 罗田县| 祁连县| 松原市| 泾源县| 平乡县| 霍州市| 乌拉特后旗|