心心念唸的AOP

面向切面編程

AOP = 面向切面編程

AOP並不是Spring框架特有的功能,不依賴於Spring也可以實現AOP,只是Spring很好的支持了AOP!

常規的數據處理流程大致是:

註冊:前端頁面 -----> Controller -----> Service -----> Mapper

登錄:前端頁面 -----> Controller -----> Service -----> Mapper

改密:前端頁面 -----> Controller -----> Service -----> Mapper

假設,需要在處理每種/每次請求時,都需要執行相同的某個任務,例如“統計業務層代碼的執行耗時”,在傳統的做法中,可以將“統計耗時”的代碼封裝在某個方法中,然後,在業務層的reg()login()changePassword()中均調用這個“統計耗時”的方法即可!但是,在比較複雜的項目中,涉及的業務可能有成百上千個,就需要在成百上千個業務方法中都添加調用“統計耗時”的方法,不便於統一管理!

面向切面編程,是在數據處理流程中,假設存在某個切面,這個切面可以在任何位置,例如在ControllerService之間,或在ServiceMapper之間,甚至可以同時存在多個切面,每個切面中都可以添加方法,當數據處理流程執行到切面時,就會執行切面中的方法!

在使用面向切面的編程時,只需要確定切面的位置,及切面中需要執行的代碼即可,對原有的ControllerService等組件中的代碼不需要進行任何調整!

首先,需要添加aspectjtoolsaspectjweaver依賴:(Springboot中居然沒有自帶這兩個依賴? 是否說明AOP並不常用?)

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

暫定目標爲“統計業務層代碼的執行耗時”。

cn.tedu.store包下創建aop包,用於存放切面類(當然,也可以使用其它包名,例如aspect),並在這個包中創建TimerAspect類(切面類推薦使用Aspect作爲類名的最後一個單詞)。作爲一個切面類,必須添加@Aspect註解,同時,是由Spring管理的,所以,還需要添加@Component註解:

package cn.tedu.store.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimerAspect {

}

然後,在切面類中,自定義方法,實現切面中的功能,並通過註解配置切面的位置:

@Around("execution(* cn.tedu.store.service.impl.*.*(..))")
public Object aaaaa(ProceedingJoinPoint pjp) throws Throwable {
    // 記錄開始時間
    long start = System.currentTimeMillis();

    // 執行業務方法,例如註冊業務、登錄業務等
    Object obj = pjp.proceed();

    // 記錄結束時間
    long end = System.currentTimeMillis();
    System.err.println("執行耗時:" + (end - start) + "毫秒。");

    // 返回
    return obj;
}

以上代碼中,註解的配置值execution(* cn.tedu.store.service.impl.*.*(..))將確定切面的位置,這段值表示“在cn.tedu.store.service.impl包下的所有類(包名右側的第1個星號)的所有方法(包名右側的第2個星號),且無視方法的參數數量(括號中的2個小數)和方法的返回值類型(包名左側的星號)”,也就是當前項目中所有的業務方法都會匹配上!

以上代碼中,使用的@Around註解表示切面的連接方法,@Around表示“包裹”,也就是在應用切面的業務方法之前和之後都存在!也可以使用@Before表示“之前”,則切面在某位置之前,也可以使用@After表示“之後”。

以上代碼中,自定義的方法必須添加ProceedingJoinPoint接口類型的參數,該參數的proceed()方法就相當於切面所包裹的位置(業務)需要執行方法,所以,可以通過pjp.proceed()表示“註冊業務”,也可以表示“登錄業務”或任何其它業務!

需要注意的是:該方法將拋出異常,用於表示具體的業務方法可能拋出的異常,例如“註冊”時拋出的UsernameDuplicateException,在切面中,必須將該異常拋出,否則,就必須使用try...catch進行捕獲,相當於自行處理了異常,後續控制器類就不會發現這個異常了!另外,調用pjp.proceed()方法將得到一個Object類型的返回值,這個返回值就相當於業務方法的返回值,例如“登錄”成功後將返回一個User對象,加載收貨地址列表時將返回一個List<Address>,在自定義切面方法時,必須獲取該返回值,並且作爲切面方法的返回值,否則,就相當於業務方法返回了null

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章