跳至主要內容
CITY DESK
科技 設計評論

一段日誌被搬走了:Java 切面編程,是一場程式碼的排版設計

從一篇掘金 AOP 教學的程式碼對照看起,觀察切面編程如何把散落各處的重複邏輯,重新設計成一套乾淨的程式介面。

SHAO MEDIA 新聞中心 閱讀約 6 分鐘

2026 年 9 月 2 日,掘金上出現一篇標題樸素的教學長文:Java 切面編程(AOP)詳解,從核心概念到實戰應用。這類文章在技術社羣裡從不罕見,罕見的是它把「為什麼需要 AOP」這件事,用兩段並排的程式碼講得異常清楚。一段是每個業務方法裡都塞滿日誌與計時的寫法,一段是抽走這些程式碼之後、乾淨得只剩業務邏輯的方法。同一件事,兩種排版,閱讀起來的感受完全不同。

這其實是一個設計問題。把它當設計看,比把它當語法看,更能理解 AOP 在解決什麼。

先從那兩段程式碼的「版面」看起

原始碼裡的 createOrder 方法,本體可能只有十行,但被三層東西包裹:開頭記參數、結尾記耗時、例外時記錯誤。try、catch、finally 三個區塊裡,真正的業務邏輯被壓縮到中間幾行,四周全是樣板。下一篇 cancelOrder 又把同一個模板原封不動抄一遍。

文章算了一筆帳:系統裡有一百個這樣的方法,日誌程式碼就重複一百遍;哪天要改日誌格式,就得到一百個地方動手。這是教科書對「橫切關注點」的標準描述,但換個角度讀,它說的其實是排版失序:有一種內容(日誌、計時、校驗)本質上是全站統一的,卻被逐字複製進每一頁,於是任何一次改版都變成地毯式工程。

報紙處理同樣的問題,用的是頁眉頁腳和欄位模板;文件處理同樣的問題,用的是樣式表。AOP 對程式碼做的事情,結構上非常接近:把重複出現的周邊內容抽成一層,套用在指定範圍上,本文回歸純淨。

切點表達式:一套被設計過的選擇語法

抽走之後,關鍵問題變成「套用到哪裡」。AOP 的答案叫切入點(Pointcut),用一種類似萬用字元的表達式描述目標,例如 execution 開頭的那串語法,指定某個套件下、某種簽名的方法。

這套語法本身就是一個值得看的設計物件。它必須夠精確,才不會誤傷無關方法;又必須夠抽象,才不用逐一列舉。文章用整節篇幅拆解 execution 的每個片段怎麼寫,因為這裡的每個萬用字元,都在決定這個切面的「作用範圍」畫在哪裡。範圍畫得太窄,切面失去意義;畫得太寬,效能與可讀性一起賠上。

通知(Advice)的設計則是時間軸上的排版:Before、After、Around、AfterReturning、AfterThrowing,五種類型對應方法執行前後的不同時點。Around 最強也最危險,它把整個方法的執行權交到切面手裡,何時呼叫 proceed()、要不要吞掉例外,全由設計者決定。權力越大,責任越重,這也是文章後半「常見坑」一節反覆出現的主題。

這種「宣告式描述意圖,讓框架處理細節」的思路,與我們先前談過的Jetpack Compose 介面設計觀察是同一個方向的兩次落地:開發者寫下「是什麼」,重複的「怎麼做」交給底層。

代理:同一個介面,兩種實作表情

文章中段花了相當篇幅比較 Spring AOP 與 AspectJ、JDK 動態代理與 CGLIB。這組比較特別值得停下來看,因為它示範了同一個抽象目標,可以由兩種工程取捨達成。

Spring AOP 走執行期動態代理:目標物件被包一層代理,呼叫端拿到的還是同一個介面,切面邏輯在代理層生效。好處是不動編譯流程、與容器整合自然;代價是隻能攔截走代理的呼叫,類內部自己呼叫自己的方法,切面就攔不到。AspectJ 走織入路線,編譯期或載入期就把切面程式碼織進目標位元組碼,攔截範圍完整,但工具鏈與維護成本都更高。

這就像同樣要為一棟建築加裝門禁,一種做法是在大廳設櫃檯(進出的都經過),一種做法是把門鎖直接換進每一扇門(沒有繞過的餘地)。兩者都成立,差別在你能接受哪一種死角。文章沒有給出唯一答案,只把各自的邊界畫清楚,這反而是誠實的寫法。

實戰案例:切面如何與其他設計接榫

後半段的三個實戰案例,展示了切面在真實系統裡的位置。日誌切面是入門款;接口耗時與效能監控,把 Around 的計時能力做成可量測的營運介面;參數校驗切面則提到了與 ValidX 的結合。

ValidX 這個名字在本站出現過:我們先前寫過ValidX 語言包的介面觀察,看它如何把錯誤訊息設計成隨語言切換的介面。校驗與切面接在一起,恰好構成一條完整的思路:進入業務之前,先在一個統一的層次把「不合法的輸入」和「友善的錯誤訊息」都處理掉,業務方法裡就只剩值得寫的邏輯。

@Order 那一節處理的是多個切面疊加時的順序問題。切面一旦不只一個,執行先後就成為新的設計變數:日誌應該在校驗之前還是之後,監控應該包在最外層還是最內層,答案取決於你想量到什麼。這是排版問題的最後一層:內容抽走了,內容之間的先後關係仍需要有人決定。

結語:乾淨是被設計出來的

這篇教學最有力量的地方,不在術語表,而在前後對照的那兩段程式碼。它讓人看見「乾淨的業務方法」並非天生,而是把一百處重複搬進一處之後的結果。

AOP 常被當成一個進階知識點來背:幾個概念、幾種通知、幾條表達式。但它的核心動作其實很樸素,辨認出散落各處的同一種內容,把它集中到一個可以被單獨修改的地方。報紙的版面、樣式表、代理層,做的是同一件事。程式碼的整潔,從來都是排版設計的成果,而非刪減的成果。

至於代價,文章也沒迴避:代理攔不到的內部呼叫、表達式畫錯範圍、Around 吞例外的隱患。任何把邏輯從眼前搬走的設計,都會換來「看不見它在哪裡」的新問題。這是所有抽象化的固定匯率,用了多少簡潔,就得付出多少追蹤成本。理解這一點的人,才真正讀懂了那兩段並排的程式碼。

#javaaop+程式碼設計#spring切面+介面設計#橫切關注點+軟體工程