什麼是 AOP?
面向切面程式設計(Aspect-Oriented Programming,AOP)旨在解決物件導向難以優雅處理的橫切關注點:日誌、交易、安全、快取、效能監控等。這些關注點橫跨多個類別與方法,在 OOP 中只能透過繼承、裝飾模式或重複程式碼處理,導致耦合度高、違反單一職責原則。
術語釐清
| 術語 | 說明 |
| Aspect(切面) | 模組化的橫切關注點實作 |
| Join Point(連接點) | 程式執行中可插入切面的位置(方法呼叫、例外拋出、欄位存取等) |
| Pointcut(切入點) | 選擇一組連接點的述語 |
| Advice(通知/增強) | 在連接點執行的動作(Before/After/Around/Throws) |
| Weaving(織入) | 將切面套用至目標程式碼的過程 |
織入時機與策略
靜態織入(編譯期/類別載入期)
- 原始碼織入:AspectJ 模式,解析原始碼 → 插入切面程式碼 → 編譯
- 位元組碼/IL 織入:編譯後修改 IL/bytecode
- 優點:執行效能最佳,無執行期額外負擔
- 缺點:修改織入規則需重新編譯;除錯對映複雜
動態織入(執行期)
- Proxy/攔截器模式:以動態代理攔截方法呼叫(如 Castle DynamicProxy、Spring.NET)
- ContextBoundObject:.NET 內建截獲機制,需繼承特定基類
- Profiler API / CLR Hook:底層插桿,功能最強但複雜度高
- 優點:無需重新編譯,支援熱部署、執行時態切面啟停
- 缺點:效能損耗;無法攔截非虛擬/靜態/建構子方法
C# 實作 AOP 的獨特挑戰
語言層面限制
- C# 無內建 AOP 語法(對比 AspectJ 的
aspect 關鍵字)
- 非虛擬方法預設不可攔截
- 結構體、密封類別、靜態方法無法以繼承/代理方式擴展
- 泛型型別在執行期具體化增加織入複雜度
生態系統成熟度
Java 擁有 AspectJ(成熟、效能佳、工具完善),.NET 早期僅有實驗性專案:
- LOOM.NET:IL 織入,專案停止維護
- Aspect#:原始碼織入,未達生產級
- Spring.NET AOP:Proxy 型,功能受限於介面/虛擬方法
- PostSharp:後編譯 IL 織入,現為 .NET AOP 事實標準(商業授權)
.NET Framework 隱含的 AOP 設計
雖無顯性 AOP 支援,但框架內建機制體現切面思想:
- ContextBoundObject + 屬性:截獲訊息鏈
- RealProxy / TransparentProxy:遠端/透明代理
- Enterprise Services (COM+):屬性驅動的交易、物件池、安全
- ASP.NET HTTP Module/Handler:管線攔截
- Attribute + Reflection:聲明式程式設計基礎
核心權衡:效能 vs 靈活性
靜態織入(編譯期 IL 修改)效能接近原生,但部署剛性;動態代理彈性高但每次呼叫多額外委派開銷。選擇取決於:
- 切面是否頻繁變更?
- 目標方法是否為熱路徑?
- 是否需要攔截非虛擬/靜態方法?
- 團隊對建置流程複雜度的容忍度?
下一步探索方向
- 原始碼層級織入器原型(借鑑 SharpDevelop/NRefactory 解析器)
- 基於屬性的聲明式切面定義(XML/Attribute 混合)
- 混合策略:熱路徑靜態織入,管理型切面動態代理
- 與現有 DI 容器整合
結語
AOP 在 C# 非語言原生支援,但透過 IL 織入(PostSharp)或動態代理可達生產級。關鍵在於識別真正的橫切關注點,避免過度設計。下篇將深入織入器實作細節與效能基準測試。
- 上一篇文章: Visual C# 2005 Express Edition Beta 首日體驗:安裝、配置與除錯陷阱
- 下一篇文章: C# 語言筆記:抽象類別限制與 out 參數型別安全
Copyright ©
程式碼之家 All Rights Reserved