首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring AOP 核心概念速成:切面、切點、連接點、通知類型與執行順序控制

     編輯:Java 綜合教學

AOP 核心術語對照表

六種通知類型與適用場景

術語英文通俗理解代碼體現
切面Aspect橫切關注點的模塊化封裝@Aspect 註解的類
連接點JoinPoint程式執行過程中可插入切面的具體點(方法調用、異常拋出等)目標方法調用時刻
切點Pointcut匹配連接點的**表達式**,定義「在哪裡織入」execution(* com.xxx.service.*.*(..))
通知Advice在切點處執行的**具體邏輯**@Before/@After 等方法體
目標對象Target被代理的業務對象Service 實現類 Bean
織入Weaving將通知應用到目標對象創建代理的過程Spring:運行時動態代理

連接點 API 實戰:獲取方法簽名、參數、目標對象

@Aspect @Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

    @Pointcut("execution(* com.example.service..*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void logBefore(JoinPoint jp) {
        Signature sig = jp.getSignature();  // 方法簽名
        String methodName = sig.getName();
        String className = sig.getDeclaringTypeName();
        Object[] args = jp.getArgs();       // 實際參數
        Object target = jp.getTarget();     // 目標對象實例
        log.info("▶ {}.{} args={}", className, methodName, Arrays.toString(args));
    }

    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfter(JoinPoint jp, Object result) {
        log.info("■ {}.{} result={}", jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), result);
    }

    @Around("serviceLayer()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        try {
            return pjp.proceed();  // **必須調用**,否則目標方法不執行
        } finally {
            long cost = (System.nanoTime() - start) / 1_000_000;
            log.info("⏱ {}.{} took {} ms", pjp.getSignature().getDeclaringTypeName(), pjp.getSignature().getName(), cost);
        }
    }
}

切點表達式語法速查

execution(modifiers? return_type package.Class.method(args))

// 常用模式
execution(* com.example.service.*.*(..))           // service 包所有類所有方法
execution(* com.example.service.UserService.*(..)) // 指定類所有方法
execution(* com.example.service.*.save*(..))       // 以 save 開頭的方法
execution(* *(..))                                 // 所有方法(慎用)
execution(!private * *(..))                        // 非私有方法
execution(public * *(@Transactional ..))           // 參數帶註解的方法

切點表達式復用與組合

@Aspect @Component
public class CommonPointcuts {
    @Pointcut("execution(* com.example.service..*(..))")
    public void inServiceLayer() {}

    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactional() {}

    @Pointcut("inServiceLayer() && transactional()")
    public void transactionalInService() {}
}

// 其他切面引用
@Aspect @Component
public class TxAspect {
    @Around("com.example.aop.CommonPointcuts.transactionalInService()")
    public Object manageTx(ProceedingJoinPoint pjp) throws Throwable { ... }
}

多切面執行順序控制:@Order

  • 數值**越小優先級越高**(先織入)。
  • 環繞通知:@Order(1) 的 @Around **最外層**包裹 @Order(2) 的 @Around。
  • 前置通知:@Order(1) 的 @Before **先於** @Order(2) 執行。
  • 後置/返回/異常通知:@Order(1) **後於** @Order(2) 執行(棧式展開)。
@Aspect @Component @Order(1)  // 最外層事務
public class TransactionAspect { ... }

@Aspect @Component @Order(2)  // 內層日誌
public class LoggingAspect { ... }

JoinPoint vs ProceedingJoinPoint 選擇指南

  • JoinPoint:用於 @Before、@After、@AfterReturning、@AfterThrowing,**只讀**連接點信息。
  • ProceedingJoinPoint:僅用於 @Around,**必須調用 proceed()** 繼續執行目標方法,可決定是否執行、執行幾次、捕獲異常、修改參數/返回值。

常見坑點

通知註解執行時機典型用途參數
前置@Before目標方法**執行前**權限校驗、參數檢查、日誌記錄JoinPoint
後置@After目標方法**執行後**(無論正常/異常)資源清理、釋放鎖、計時結束JoinPoint
返回@AfterReturning目標方法**正常返回後**結果加工、緩存寫入、審計日誌JoinPoint + returning
異常@AfterThrowing目標方法**拋出異常後**異常統一處理、錯誤上報、補償邏輯JoinPoint + throwing
環繞@Around**完全控制**目標方法執行(前/後/異常/不執行)事務管理、性能監控、重試、緩存攔截ProceedingJoinPoint
引入@DeclareParents為目標類動態添加父類/接口極少用,混入功能--
坑點現象解決
@Around 忘記 proceed()目標方法不執行,返回 null必寫 return pjp.proceed(); 或手動返回值
proceed() 拋異常未處理異常傳播導致事務不回滾try-finally 包裹、或拋出 Throwable 讓 Spring 統一處理
切點表達式過寬誤攔截內部方法、Getter/Setter、性能損耗精確到包/類/方法前綴、排除 getter/setter
同類內部方法調用AOP 失效(繞過代理)注入自身 AopContext.currentProxy() 或拆分類
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved