| 術語 | 說明 |
|---|---|
| Aspect (切面) | 橫切關注點的模組化單元 (如日誌、事務、安全) |
| Join Point (連接點) | 程式執行過程中的某個點 (方法呼叫、異常拋出、欄位存取) |
| Pointcut (切點) | 匹配連接點的謂詞表達式 |
| Advice (通知/增強) | 切面在特定切點執行的動作 |
| Target Object (目標物件) | 被代理的物件 |
| Weaving (織入) | 將切面應用到目標物件的過程 (編譯期/類載入期/運行期) |
| 類型 | 註解 | XML 元素 | 執行時機 |
|---|---|---|---|
| Before | @Before | <aop:before> | 目標方法執行前 |
| After | @After | <aop:after> | 目標方法執行後 (無論正常/異常) |
| After Returning | @AfterReturning | <aop:after-returning> | 目標方法正常返回後 |
| After Throwing | @AfterThrowing | <aop:after-throwing> | 目標方法拋出異常後 |
| Around | @Around | <aop:around> | 包圍目標方法,可控制是否執行、修改參數/返回值 |
<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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
需額外引入 AspectJ JAR:aspectjrt.jar、aspectjweaver.jar、aspectj.jar
package com.example.aop;
public class LoggingAspect {
public void beforeAdvice() { System.out.println("Before: Setup profile"); }
public void afterAdvice() { System.out.println("After: Profile ready"); }
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning: " + retVal);
}
public void afterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("Exception: " + ex);
}
}
package com.example.aop;
public class Student {
private String name;
private int age;
// getter/setter...
public void printThrowException() { throw new IllegalArgumentException(); }
}
<aop:config>
<aop:aspect id="logAspect" ref="logging">
<!-- 切點:匹配 com.example.aop 包下所有類的所有方法 -->
<aop:pointcut id="allMethods" expression="execution(* com.example.aop.*.*(..))"/>
<aop:before pointcut-ref="allMethods" method="beforeAdvice"/>
<aop:after pointcut-ref="allMethods" method="afterAdvice"/>
<aop:after-returning pointcut-ref="allMethods" returning="retVal" method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="allMethods" throwing="ex" method="afterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<bean id="student" class="com.example.aop.Student">
<property name="name" value="Alice"/>
<property name="age" value="20"/>
</bean>
<bean id="logging" class="com.example.aop.LoggingAspect"/>
execution(modifiers? return_type package.ClassName.methodName(parameters) throws?)
| 範例 | 含義 |
|---|---|
execution(* com.example.*.*(..)) | 包下所有類所有方法 |
execution(* com.example.Student.getName(..)) | 指定類指定方法 |
execution(* com.example.service.*.*(..)) | service 包下所有方法 |
execution(* *(..)) | 所有方法 (慎用) |
<aop:aspectj-autoproxy/> <!-- XML 中聲明 -->
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {}
package com.example.aop;
import org.aspectj.lang.annotation.*;
@Aspect
public class LoggingAspect {
@Pointcut("execution(* com.example.aop.*.*(..))")
private void allMethods() {}
@Before("allMethods()")
public void beforeAdvice() { System.out.println("Before: Setup profile"); }
@After("allMethods()")
public void afterAdvice() { System.out.println("After: Profile ready"); }
@AfterReturning(pointcut = "allMethods()", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning: " + retVal);
}
@AfterThrowing(pointcut = "allMethods()", throwing = "ex")
public void afterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("Exception: " + ex);
}
@Around("allMethods()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around: Before");
Object ret = pjp.proceed(); // 執行目標方法
System.out.println("Around: After");
return ret;
}
}
<aop:aspectj-autoproxy/>
<bean id="student" class="com.example.aop.Student">...</bean>
<bean id="logging" class="com.example.aop.LoggingAspect"/>
@Around("execution(* com.example.service.*.*(..))")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed(); // 必須呼叫,否則目標方法不執行
} finally {
long elapsed = System.nanoTime() - start;
System.out.println(pjp.getSignature() + " took " + elapsed + " ns");
}
}
pjp.proceed(newArgs)// XML 配置匹配所有方法
Before: Setup profile
Name: Alice
After: Profile ready
Returning: Alice
Before: Setup profile
Age: 20
After: Profile ready
Returning: 20
Before: Setup profile
Exception: java.lang.IllegalArgumentException
After: Profile ready
Exception: java.lang.IllegalArgumentException
// 修改切點僅匹配 getName()
Before: Setup profile
Name: Alice
After: Profile ready
Age: 20
Exception: java.lang.IllegalArgumentException
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 織入時機 | 運行期 (代理) | 編譯期/類載入期 |
| 支援連接點 | 僅方法執行 | 欄位存取、建構子、靜態初始化等 |
| 目標物件 | Spring Bean | 任意物件 |
| 效能 | 代理開銷 | 無運行期開銷 |
| 複雜度 | 低 | 高 (需 AJC 編譯器) |
execution(* *(..)) 全匹配,影響效能與副作用