面向切面程式設計(AOP)將橫切關注點(日誌、事務、安全)從業務邏輯中分離。Spring AOP 基於動態代理,支援 JDK 動態代理(介面)與 CGLIB(類)兩種模式。本文以 Spring 4.x XML 配置為例,示範最基礎的「前置/後置通知」實作。
<dependencies>
<!-- Spring Core/Context/AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.25.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.25.RELEASE</version>
</dependency>
<!-- AOP 必備:AspectJ Weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
package com.wang.service;
public class UserService {
public void add() { System.out.println("新增用戶"); }
public void delete() { System.out.println("刪除用戶"); }
public void update() { System.out.println("修改用戶"); }
public void search() { System.out.println("查詢用戶"); }
}
package com.wang.log;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + " 的 " + method.getName() + " 方法開始執行");
}
}
package com.wang.log;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + " 的 " + method.getName() + " 方法執行成功,回傳值:" + returnValue);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 目標 Bean -->
<bean id="userService" class="com.wang.service.UserService" />
<!-- 通知 Bean -->
<bean id="log" class="com.wang.log.Log" />
<bean id="afterlog" class="com.wang.log.AfterLog" />
<aop:config>
<!-- 切入點:攔截 userService 所有無參方法 -->
<aop:pointcut expression="execution(* com.wang.service.UserService.*())" id="pointcut" />
<!-- 綁定前置通知 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
<!-- 綁定後置通知 -->
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut" />
</aop:config>
</beans>
@Test
public void testAop() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.add();
userService.search();
}
com.wang.service.UserService 的 add 方法開始執行 新增用戶 com.wang.service.UserService 的 add 方法執行成功,回傳值:null com.wang.service.UserService 的 search 方法開始執行 查詢用戶 com.wang.service.UserService 的 search 方法執行成功,回傳值:null
| 術語 | 本例對應 | 說明 |
|---|---|---|
| 切面 | Log / AfterLog 類 | 橫切邏輯的程式碼實作 |
| 通知 | before() / afterReturning() | 具體執行動作與時機 |
| 切入點 | execution(* com.wang.service.UserService.*()) | 定義「在哪裡」織入 |
| 連接點 | add()/delete()/update()/search() | 程式執行的具體位置 |
| 織入 | Spring 容器啟動時建立代理 | 將切面應用到目標物件的過程 |
現代 Spring 專案建議使用 @Aspect + @Before/@AfterReturning 註解風格,配合 @EnableAspectJAutoProxy 啟用,配置更直觀、重構更安全。XML 風格僅適用於維護舊專案或需外部化切點定義的場景。