在軟體開發社群中,面向切面程式設計(AOP)早已不是新鮮事。Spring、JBoss AOP 等成熟框架將其推向主流,但對於初學者而言,OOP 尚未精通,AOP 又添新概念,難免讓人感到困惑。本文不探討晦澀的理論定義,而是透過一個精簡的實例,展示如何利用 Java 核心類庫中的動態代理機制,從零開始實作 AOP 的核心邏輯——將「日誌記錄」等橫切關注點從業務邏輯中剝離。
假設我們有一個處理業務邏輯的介面 BusinessInterface 及其實作類 BusinessObject。在傳統 OOP 寫法中,日誌記錄程式碼會散落在每個業務方法中:
public class BusinessObject implements BusinessInterface {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void processBusiness() {
try {
logger.info("start to processing...");
// 核心業務邏輯
System.out.println("here is business logic");
logger.info("end processing...");
} catch (Exception e) {
logger.info("exception happens...");
}
}
}
這樣做有兩大弊端:
AOP 的核心思想是「分離關注點」。我們需要一種機制,在業務方法被調用的「前」與「後」自動插入日誌邏輯。JDK 1.3 引入的 java.lang.reflect.Proxy 與 InvocationHandler 正是為此而生。
實作 InvocationHandler 介面,在 invoke 方法中編寫通用的「前置/後置」邏輯:
public class LogHandler implements InvocationHandler {
private static final Logger logger = Logger.getLogger(LogHandler.class.getName());
private final Object target; // 被代理的真實業務物件
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
logger.info("方法開始執行: " + method.getName());
// 核心:透過反射調用真實業務方法
result = method.invoke(target, args);
logger.info("方法執行結束: " + method.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "方法拋出異常: " + method.getName(), e);
throw e.getCause(); // 保留原始堆疊追蹤
}
return result;
}
}
業務類別 BusinessObject 現在可以專注於核心邏輯,完全移除日誌程式碼:
public class BusinessObject implements BusinessInterface {
public void processBusiness() {
// 純粹的業務邏輯
System.out.println("here is business logic");
}
}
客戶端使用 Proxy.newProxyInstance 動態生成實作 BusinessInterface 的代理類別:
public class Client {
public static void main(String[] args) {
// 1. 真實業務物件
BusinessInterface realObject = new BusinessObject();
// 2. 建立處理器,傳入真實物件
InvocationHandler handler = new LogHandler(realObject);
// 3. 產生代理物件
BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(
realObject.getClass().getClassLoader(),
realObject.getClass().getInterfaces(),
handler
);
// 4. 對外呼叫介面完全一致,但自動織入了日誌
proxy.processBusiness();
}
}
控制台輸出:
資訊: 方法開始執行: processBusiness
here is business logic
資訊: 方法執行結束: processBusiness
透過這個簡單範例,我們達成了 AOP 的核心價值:
LogHandler,業務類別零汙染。LogHandler 可服務所有實作 BusinessInterface 的類別。JDK 動態代理有兩大限制:
InvocationHandler。若同時需要「日誌 + 事務 + 效能統計」,需自行實作責任鏈模式串聯多個攔截器。這正是 Spring AOP、AspectJ 等框架存在的意義:它們封裝了代理建立、切點表達式匹配、多攔截器排序織入等複雜細節,提供聲明式(註解/XML)配置,讓開發者專注於業務切面邏輯本身。
最佳實踐建議:學習階段手寫動態代理有助於理解 AOP 底層原理;實際專案請直接使用 Spring AOP(基於代理)或 AspectJ(編譯期/類載入期織入),享受成熟生態與完整功能支援。