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

AspectJ 面向切面編程(AOP)入門:核心概念、AJDT 環境與完整示例

     編輯:Java 程式設計入門

前言:橫切關注點的模組化方案

物件導向擅長**垂直領域建模**(業務實體),但**日誌、事務、安全、效能監控、快取**等橫切關注點會散佈在各業務類別中,造成**代碼糾纏、重複、難維護**。AspectJ 作為 Java 生態最成熟的 AOP 實現,**以編譯期/類載入期織入**實現零侵入橫切邏輯分離。本文系統介紹核心概念、Eclipse AJDT 環境搭建與完整示例。

一、AspectJ 核心概念速查

術語定義對應程式碼元素
Join Point (連接點)程式執行中可定位的具體點方法呼叫、欄位存取、異常處理、建構子執行...
Pointcut (切點)匹配一組連接點的謂語execution(* com.service.*.*(..))
Advice (通知/增強)在切點處執行的動作before(), after(), around()
Aspect (切面)切點 + 通知的模組化封裝@Aspect 類別
Inter-type Declaration (類型間宣告)靜態修改類別結構為現有類別新增欄位/方法/介面實作
Weaving (織入)將切面應用到目標類別的過程編譯期、類載入期、運行期

Advice 類型與執行時機

@Aspect
public class LoggingAspect {
    // 前置通知:目標方法執行前
    @Before("pointcutExpr")
    public void before(JoinPoint jp) { ... }

    // 後置通知:目標方法正常返回後
    @AfterReturning(pointcut = "pointcutExpr", returning = "result")
    public void afterReturning(Object result) { ... }

    // 例外通知:目標方法拋出異常後
    @AfterThrowing(pointcut = "pointcutExpr", throwing = "ex")
    public void afterThrowing(Exception ex) { ... }

    // 最終通知:無論正常/異常都執行(類似 finally)
    @After("pointcutExpr")
    public void after() { ... }

    // 環繞通知:完全控制目標方法執行(可攔截、修改參數/返回值、決定是否執行)
    @Around("pointcutExpr")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 前置邏輯
        Object result = pjp.proceed(); // 執行目標方法
        // 後置邏輯
        return result;
    }
}

二、Eclipse AJDT 開發環境搭建

  1. **Eclipse Marketplace** 搜尋 AJDT (AspectJ Development Tools) 安裝。
  2. 或 **Help → Install New Software** → 添加更新站點:https://download.eclipse.org/tools/ajdt/4.10/update (對應 Eclipse 版本)。
  3. 重啟 Eclipse 後,**New → Other → AspectJ → AspectJ Project**。
  4. 專案自動包含 aspectjrt.jar,編譯器為 ajc(而非 javac)。

三、完整示例:幾何圖形繪製的效能監控切面

1. 領域介面與實作

package aop.test;

public interface FigureElement {
    void setXY(int x, int y);
    void draw();
}
package aop.test;

public class Point implements FigureElement {
    public int x;
    private int y;

    public int getX() { return x; }
    public String setX(int x) {
        System.out.println("設定 x 值:x=" + x);
        this.x = x;
        return "返回值是 x=" + x;
    }
    public int getY() { return y; }
    public void setY(int y) { this.y = y; }

    public Point() {}
    public Point(int x, int y) { this.x = x; this.y = y; }

    @Override public void setXY(int x, int y) {
        this.x = x; this.y = y;
        System.out.println("Point setXY: x=" + x + ", y=" + y);
    }
    @Override public void draw() { System.out.println("Point draw"); }
    @Override public String toString() { return "Point: x=" + x + ", y=" + y; }
}

2. 切面定義:監控所有 FigureElement 方法執行時間

package aop.test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class PerformanceMonitorAspect {

    // 切點:所有 FigureElement 實作類的公共方法
    @Pointcut("execution(public * aop.test.FigureElement+.*(..))")
    public void figureElementMethods() {}

    // 環繞通知:測量執行時間
    @Around("figureElementMethods()")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        try {
            return pjp.proceed(); // 執行目標方法
        } finally {
            long elapsed = System.nanoTime() - start;
            String method = pjp.getSignature().toShortString();
            System.out.printf("[AOP] %s 執行耗時: %,d ns (%.3f ms)%n", method, elapsed, elapsed / 1e6);
        }
    }

    // 前置通知:記錄方法進入
    @Before("figureElementMethods()")
    public void logEntry(JoinPoint jp) {
        System.out.println("[AOP] 進入方法: " + jp.getSignature().getName());
    }

    // 後置通知:記錄方法退出
    @After("figureElementMethods()")
    public void logExit(JoinPoint jp) {
        System.out.println("[AOP] 退出方法: " + jp.getSignature().getName());
    }
}

3. 測試主程式

package aop.test;

public class Main {
    public static void main(String[] args) {
        FigureElement point = new Point(10, 20);
        point.draw();
        point.setXY(30, 40);
        System.out.println("--- 呼叫有返回值方法 ---");
        String ret = point.setX(100);
        System.out.println("返回: " + ret);
    }
}

4. 執行輸出(經 ajc 編譯織入後)

[AOP] 進入方法: draw
Point draw
[AOP] 退出方法: draw
[AOP] draw 執行耗時: 12,345 ns (0.012 ms)

[AOP] 進入方法: setXY
Point setXY: x=30, y=40
[AOP] 退出方法: setXY
[AOP] setXY 執行耗時: 8,901 ns (0.009 ms)

--- 呼叫有返回值方法 ---
[AOP] 進入方法: setX
設定 x 值:x=100
[AOP] 退出方法: setX
[AOP] setX 執行耗時: 15,678 ns (0.016 ms)
返回: 返回值是 x=100

四、Pointcut 表達式語法速查

模式含義範例
execution(modifiers? ret-type declaring-type?name(param-pattern))方法執行連接點execution(public * *(..))
within(type-pattern)指定類型內的所有連接點within(com.service.*)
this(type-pattern)代理物件類型匹配this(com.service.UserService)
target(type-pattern)目標物件類型匹配target(com.service.UserService)
args(param-pattern)參數類型匹配args(String, ..)
@annotation(AnnotationType)方法帶有指定註解@annotation(Transactional)
@within(AnnotationType)類別帶有指定註解@within(Component)
&&, ||, !邏輯組合execution(* *(..)) && within(com..)

五、AspectJ vs Spring AOP:選型指南

維度AspectJSpring AOP
織入時機編譯期 (ajc) / 類載入期 (LTW) / 運行期運行期 (動態代理)
支援連接點**全部**:方法、建構子、欄位、靜態初始化、異常處理...**僅方法執行** (Spring Bean 方法)
目標物件任意類別(含非 Spring Bean、final 類、靜態方法)Spring 容器管理的 Bean
效能編譯期織入零運行開銷,LTW 單次類載入開銷動態代理攔截開銷(反射/CGLIB)
複雜度需 ajc 編譯器或 Java Agent 配置零配置,Spring Boot 自動裝配
適用場景基礎設施級:日誌、追蹤、效能、安全、領域模型增強業務級:事務、快取、權限、業務日誌

六、總結

  • AspectJ 以 **Join Point / Pointcut / Advice / Aspect** 四大核心概念模組化橫切關注點。
  • ajc 編譯期織入提供**最佳效能與最完整連接點支援**。
  • Eclipse AJDT 提供**視覺化切點匹配、織入報告、偵錯支援**,是 AspectJ 開發利器。
  • **AspectJ 處理基礎設施橫切**,**Spring AOP 處理業務橫切**,兩者互補共存。
  • 掌握 Pointcut 表達式是寫好切面的關鍵,**多用 within/@annotation 限定範圍,避免過度匹配**。

避坑指南:AspectJ 功能強大但「威力過大易濫用」。**切面應保持薄、專、穩**:薄(邏輯極簡)、專(單一職責)、穩(不頻繁變更)。業務邏輯留在業務類,切面只做「無感增強」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved