AOP 是學習整個 Spring 框架內的核心之一,基本上你用 Spring 的東西很難多多少少沒有接觸過一點AOP,最簡單能接觸到的大概就是 Transactional 了。
我們都知道上了 Transactional 這 annotation 之後可以保持我們在操作「業務邏輯」時,中間經過的「 DB 資料操作」能保證是強一致性,然後我們再深入了解基底機制一點,會再知道說這底層的操作其實就 Spring 對了我們的 class 外層再包一層強化類的proxy。
而這 proxy 呢,可能是藉由 CGlib 去實作,或者是 JDK Dynamic Proxy 來做處理,但怎麼配置基本上我們也沒在管,畢竟就像前面說的,這已經被Spring 做掉了,你就用就好。
Spring AOP 基本的專案依賴配置
就跟上頭所說的一樣,這都被 Spring 幫你做掉了,你裝上 Spring AOP 的 Starter 就能用 Spring AOP 寫切面了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>改用 AspectJ 的設定與配置
從這邊開始就是我們的重頭戲,這邊會跟技術架構的取捨有關,畢竟我們專案做到後期還是把 AspectJ 給拔掉了。但你要先會這套 lib 再做什麼才會有後面的取捨。
AspectJ 介紹
AspectJ 是一套強大的 AOP 函式庫。你一定很好奇,既然有了 Spring AOP 為何還要額外配置 AspectJ?
核心原因與 Proxy (代理模式) 的局限性有關:
私有方法 (Private Methods):Proxy 無法攔截類別內部的私有方法。
自我呼叫 (Self-invocation):當類別內部方法呼叫 this.methodB() 時,呼叫的是原始對象而非 Proxy,導致外層代理感知不到,這就是著名的 @Transactional 失效問題。
剩下什麼 Spring AOP 跟 AspectJ 差異我的就不提了,留幾個關鍵字就好,畢竟剩下的關鍵字其他人的文應該寫很多。
| compile weaving 、 post compile weaving 、 編譯時期載入 、 proxy 、 Self-invocation |
阿反正基於上述問題,所以這邊就會有需求要加這套了
將 Spring AOP 的Auto Proxy關閉
因為我們現在要將 Spring AOP 切換成 AspectJ 模式,所以我們會需要撰寫參數將 Spring AOP 的 proxy 模式關閉,這邊就直接在 application.properties 上改就好,如果你要整理的更乾淨的話就拆 properties 吧。
spring.aop.auto=false新增 AspectJ需要的 Lib
我們因為把 proxy 關掉的關係,所以 Transactional 的 annotation 會直接失效,這邊會需要將 aspectJ 版本的函式庫加進去,並且將指定版本的 AspectJ 相關 lib也加入 pom 檔當中。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.21.2</version>
</dependency>
<!-- AspectJ Weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21.2</version>
</dependency>
<!-- AspectJ tool -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.21.2</version>
</dependency>下面列個清單說明:
spring-aspects:這是 Spring 提供的整合包,裡面包含了 Spring 預定義好的切面(比如 @Transactional 的 AspectJ 版實作)。
aspectjrt (Runtime):這是執行時需要的最小函式庫。它提供了 AOP 程式碼在跑的時候需要的註解與基礎類別(如 @Aspect, JoinPoint)。
aspectjweaver:最核心的關鍵。 它負責「織入」的動作。如果你是用 LTW (Load-Time Weaving),這個 jar 就是必須掛在 JVM -javaagent 後面的那個特務(Agent)。
aspectjtools:包含了 ajc 編譯器等工具。如果你打算用 CTW (Compile-Time Weaving),在編譯時期就完成基因改造,這個 lib 就是主角。
編譯 plugin 設定
將相關的lib放入專案當中並不會直接讓專案就使用 AspectJ 去編譯,我們會需要對應的 maven 插件 或者是 gradle 插件,這邊因為我們專案用 maven ,所以我們用 maven 去作為示範,而 gradle 的化設定方法比較簡單況且有人有寫教學就不多寫了。
<plugins>
<!-- 設定 AspectJ 的編譯器 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.15.0</version>
<!-- 更新 aspectJ 的 版本到專案JDK所需要的版本 -->
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.21.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.21.2</version>
</dependency>
</dependencies>
<configuration>
<source>21</source>
<!-- Explicitly set Java version -->
<target>21</target>
<complianceLevel>21</complianceLevel>
<!-- 用 AspectJ 語言撰寫的 lib -->
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<!-- 強制使用 AspectJ 編譯器 重新編譯 -->
<forceAjcCompile>true</forceAjcCompile>
<sources/>
<!-- 重新編譯的目錄清單 -->
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
<Xlint>ignore</Xlint>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Finally, package with Spring Boot -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.4.3</version>
</plugin>
</plugins>額外說明:
aspectj-maven-plugin:
這款插件因為從2023還24年就沒更新了,依照作者文件的說法,看起來是只需要將依賴的 aspectJ lib 更新成對應 JDK 的版本就可以繼續使用了,所以我們這邊就將我們上面 pom 版本的 aspectJ 加入進去
其實後續有人維護另一個版本,叫做dev.aspectj:aspectj-maven-plugin,這版本在今天(2026/5/4)我上去github看了一下,最近的 released 時間是 今年三月,可以考慮試試。
aspectLibraries:這一步至關重要。僅僅在 pom 引入 spring-aspects 是不夠的,我們必須明確告訴 ajc 編譯器,要把 Spring 預寫好的切面(如 AnnotationTransactionAspect)織入到我們的程式碼中。這就是為什麼要在插件內重複引用它的原因。
forceAjcCompile:設定為 true 是為了確保所有的類別都會經過 ajc 處理,而不是只處理變動過的檔案。這在處理複雜的 this 呼叫織入時比較保險。
showWeaveInfo:建議在開發階段開啟。編譯時你會看到類似 Join point 'method-execution(…)'… woven into … 的日誌,這能讓你肉眼確認你的切面到底有沒有真的「織」進去。