AOP應用

1. AOP 簡介

  • 定義:面向切面編程(Aspect-Oriented Programming)是一種編程范式,允許在不修改核心業(yè)務邏輯的情況下,將橫切關(guān)注點與業(yè)務邏輯分離。
  • 目的:提高代碼的可讀性和維護性,減少重復代碼。
  • 使用場景:日志記錄、異常處理、事務管理、安全驗證、性能監(jiān)控等。

2. AOP 的核心概念

  • 切面(Aspect):封裝橫切關(guān)注點的模塊,如日志記錄功能。
  • 連接點(JoinPoint):程序執(zhí)行的某個特定點,比如方法的調(diào)用或異常的拋出。
  • 通知(Advice):切面在連接點執(zhí)行的操作,常見的類型包括前置通知、后置通知、環(huán)繞通知、異常通知等。
  • 切入點(Pointcut):定義在哪些連接點上應用通知,通過表達式來進行匹配。
  • 織入(Weaving):將切面應用到目標對象的過程。

3. AOP 的使用場景

  • 日志管理:在方法執(zhí)行前后記錄日志。
  • 性能監(jiān)控:監(jiān)控方法執(zhí)行的時間。
  • 安全控制:在方法執(zhí)行前進行權(quán)限校驗。
  • 事務管理:確保方法執(zhí)行時事務的一致性。

4. Spring AOP 實現(xiàn)

  • Spring AOP 是基于動態(tài)代理的實現(xiàn),支持基于接口和類的代理。
  • 通過 Spring AOP 的 @Aspect 注解可以輕松實現(xiàn)切面。

5. 核心場景

1.展示如何使用 Spring AOP 來記錄日志-(方法執(zhí)行前后新增日志,一般用于核心方法定位入?yún)⒑头祷亟Y(jié)果):

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBeforeMethod(JoinPoint joinPoint) {
        System.out.println("Method: " + joinPoint.getSignature().getName() + " is about to be called");
    }

    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void logAfterMethod(JoinPoint joinPoint, Object result) {
        System.out.println("Method: " + joinPoint.getSignature().getName() + " has returned with result: " + result);
    }
}

場景2 結(jié)合 Redis 實現(xiàn)冪等性保障

冪等性是指某個操作無論執(zhí)行多少次,結(jié)果都是相同的。這在支付系統(tǒng)、訂單系統(tǒng)等場景中尤為重要。結(jié)合 AOP 和 Redis,可以通過唯一標識和過期機制實現(xiàn)接口的冪等性。

也可以在切面里通過數(shù)據(jù)庫層面進行冪等,需要對冪等字段增加唯一索引。這樣如果在高頻接口會增加db壓力,建議是把db冪等放到方法里面。redis冪等放外面。雙重保證。

實現(xiàn)步驟:

  1. 在請求中引入唯一標識:可以在請求中攜帶一個唯一的 requestId
  2. 利用 Redis 存儲請求標識:在執(zhí)行方法前,先檢查 Redis 中是否已有相同的 requestId,如果存在則拒絕執(zhí)行操作。
  3. 執(zhí)行方法后刪除標識(可選):根據(jù)實際需求決定是否刪除 requestId。

代碼示例:

@Aspect
@Component
public class IdempotentAspect {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    // 定義冪等的切入點
    @Pointcut("@annotation(com.example.annotation.Idempotent)")
    public void idempotentPointcut() {}

    @Around("idempotentPointcut() && args(requestId,..)")
    public Object ensureIdempotency(ProceedingJoinPoint joinPoint, String requestId) throws Throwable {
        String key = "idempotent:" + requestId;

        // 嘗試將 requestId 存入 Redis,過期時間為 5 分鐘
        Boolean isAbsent = redisTemplate.opsForValue().setIfAbsent(key, "1", 5, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(isAbsent)) {
            throw new IllegalStateException("請求重復,已拒絕處理");
        }

        try {
            // 執(zhí)行目標方法
            return joinPoint.proceed();
        } finally {
            // 根據(jù)需求,是否需要刪除 key
            // redisTemplate.delete(key);
        }
    }
}

自定義注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {}

通過 Redis 實現(xiàn)冪等性,避免了同一個請求被重復處理,從而保護了業(yè)務邏輯的正確性,尤其在網(wǎng)絡重試或分布式系統(tǒng)中非常重要。


場景3.記錄方法執(zhí)行時間

一般應用于需要調(diào)優(yōu)的核心方法。記錄執(zhí)行時間長短
監(jiān)控方法的執(zhí)行時間是優(yōu)化性能的重要手段。通過 AOP 和 可以記錄方法的執(zhí)行時間。

代碼示例:

@Aspect
@Component
public class MethodExecutionTimeAspect {

    @Autowired
    private RedisTemplate<String, Long> redisTemplate;

    // 定義切入點,監(jiān)控 service 包下的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void monitorPointcut() {}

    @Around("monitorPointcut()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 執(zhí)行目標方法
        Object proceed = joinPoint.proceed();

        long executionTime = System.currentTimeMillis() - startTime;
        
        // 記錄執(zhí)行時間到 Redis,key 可以是方法名稱
        String methodName = joinPoint.getSignature().toShortString();

        System.out.println("Method " + methodName + " executed in " + executionTime + "ms");
        return proceed;
    }
}

6. AOP 的優(yōu)缺點

  • 優(yōu)點
    • 解耦橫切關(guān)注點與業(yè)務邏輯。
    • 代碼更簡潔、可維護性更高。
  • 缺點
    • 可能增加調(diào)試難度,特別是當多個切面交織在一起時。

7. 總結(jié)

  • AOP 是一種強大的編程范式,尤其在需要分離橫切關(guān)注點的場景中非常有用。
  • 在使用 AOP 時,要注意保持切面的簡潔,避免過度使用導致代碼復雜化。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 前言 Spring 最重要的兩個功能,就是依賴注入(DI)和面向切面編程 (AOP)。 AOP 為我們提供了...
    木子教程閱讀 1,057評論 0 4
  • AOP目的: 面向切面編程(aspect-oriented programming,AOP)主要實現(xiàn)的目的是針對業(yè)...
    逝者如斯灬閱讀 429評論 0 0
  • Aop- 面向切面編程-->通俗講 對業(yè)務方法做一些增強(比如日志輸出,事務控制,異常的處理等。。) 舉個例子:比...
    wang_cheng閱讀 431評論 0 0
  • 0. AOP是什么? AOP,面向切面編程,是對OOP的補充。面向切面編程有好幾種方式,可以在編譯時候織入,也可以...
    魯班0號閱讀 1,980評論 0 0
  • JVM-Class類文件結(jié)構(gòu) 常量池:字面量(字符串和final常量)和符號引用(類和接口的全限定名、字段的名稱和...
    小丑的果實閱讀 1,923評論 0 0

友情鏈接更多精彩內(nèi)容