在企業級開發中,**日誌記錄、事務管理、權限驗證、效能監控、快取、重試、錯誤處理**等橫切關注點散落在業務邏輯各處,導致程式碼重複、耦合度高、難以維護。AOP(Aspect-Oriented Programming)將這些橫切邏輯封裝為切面,透過切點精準織入目標方法,實現關注點分離。AspectJ 是 Java 生態最成熟、功能最強的 AOP 框架,支援註解式、XML、編譯期/載入期/執行期織入。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.21</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21</version>
</dependency>
Spring Boot 專案只需 spring-boot-starter-aop,自動傳遞依賴並啟用 @EnableAspectJAutoProxy。
| 術語 | 說明 | AspectJ 註解 |
|---|---|---|
| 切面 | 橫切邏輯模組化單元 | @Aspect |
| 切點 | 定義「在哪裡」織入的謂詞 | @Pointcut |
| 通知 | 定義「何時、做什麼」 | @Before/@After/@AfterReturning/@AfterThrowing/@Around |
| 連接點 | 程式執行過程中可織入的具體點(方法呼叫、欄位存取、例外拋出等) | — |
| 引介 | 為現有類動態新增介面/方法/欄位 | @DeclareParents |
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MonitorTime {
String value() default ""; // 業務標識
}
@Aspect
@Component // Spring 管理
@Slf4j
public class MonitorAspect {
@Pointcut("@annotation(com.example.demo.aop.MonitorTime)")
public void monitorPointcut() {}
@Around("monitorPointcut()")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
MethodSignature sig = (MethodSignature) pjp.getSignature();
MonitorTime anno = sig.getMethod().getAnnotation(MonitorTime.class);
String tag = anno.value().isEmpty() ? sig.getDeclaringTypeName() + "." + sig.getName() : anno.value();
try {
Object result = pjp.proceed(); // 執行目標方法
long costMs = (System.nanoTime() - start) / 1_000_000;
log.info("[Monitor] {} 耗時 {} ms", tag, costMs);
return result;
} catch (Exception e) {
long costMs = (System.nanoTime() - start) / 1_000_000;
log.error("[Monitor] {} 異常耗時 {} ms: {}", tag, costMs, e.getMessage(), e);
throw e;
}
}
}
@Service
public class OrderService {
@MonitorTime("下單流程")
public Order createOrder(OrderDTO dto) {
// 業務邏輯...
return orderRepository.save(new Order(dto));
}
}
// 執行 com.service 包下所有 public 方法
execution(public * com.service..*.*(..))
// 執行所有標記 @Transactional 的方法
@annotation(org.springframework.transaction.annotation.Transactional)
// 執行所有 Service 介面的實現類方法
execution(* com.service.*.*(..)) && target(com.service.BaseService)
// 組合:Service 層且非 getter/setter
execution(* com.service..*.*(..)) && !execution(* *.get*(..)) && !execution(* *.set*(..))
this.method() 繞過代理,AOP 失效。解法:注入自身 @Autowired Self self 或提取到別的 Bean。proxy-target-class=true 使用 CGLIB。ProceedingJoinPoint、反射獲取註解。高頻熱點路徑(如每秒萬次)慎用,改用手動埋點或編譯期織入。@AfterThrowing 僅觀察,若需轉換異常請用 @Around 捕獲後重新拋出。| 維度 | Spring AOP (執行期代理) | AspectJ (編譯期/載入期織入) |
|---|---|---|
| 支援連接點 | 僅方法執行 | 方法、建構子、欄位存取、靜態初始化等 |
| 部署複雜度 | 零配置,啟動即用 | 需 AJC 編譯器或 Java Agent (-javaagent:aspectjweaver.jar) |
| 效能 | 微小反射/代理開銷 | 零執行期開銷,字節碼內聯 |
| 適用場景 | 95% 業務系統 | 極高頻熱點、領域模型引介、基礎設施框架 |
AOP 是消除橫切關注點重複程式碼的利器。掌握 @Aspect、@Pointcut、五種通知類型、切點表達式語法,即可解決 90% 的 AOP 需求。記住:**切面要薄、職責單一、避免巢狀攔截、高頻路徑慎用**。配合自定義註解,能讓業務程式碼保持乾淨,同時獲得統一的可觀測性與治理能力。