首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Spring AOP @AspectJ 攔截自訂註解:同類內部方法調用不生效?代理機制限制

     編輯:程式設計綜合問答

現象

切面類定義 `@Around("@annotation(LogAnnotation)")`,Service 實現類方法加上 `@LogAnnotation`。外部呼叫 `service.a()` 觸發切面,**但 `a()` 內部呼叫 `this.b()` 時,`b()` 上的註解切面不觸發**。

根因:Spring AOP 基於動態代理

  • Spring 為 Bean 建立**代理物件**(JDK 動態代理或 CGLIB 子類)。
  • 外部呼叫 `service.a()` 實際是呼叫 **代理的 `a()`** → 進入切面 → 代理內部呼叫 `target.a()`。
  • `a()` 內部 `this.b()` 是**直接對目標物件(target)呼叫**,**繞過代理**,因此切面邏輯不執行。

解決方案(按推薦順序)

1. 重構:將需切面的方法拆分到不同 Bean(最佳實踐)

@Service
public class AService {
    @Autowired private BService bService;
    @LogAnnotation public void a() { bService.b(); }
}

@Service
public class BService {
    @LogAnnotation public void b() { ... }
}

外部呼叫 `aService.a()` → 代理 → `bService.b()` → 代理 → 切面生效。符合單一職責、解耦合。

2. 自我注入:注入自身代理(治標)

@Service
public class MyService implements ApplicationContextAware {
    private MyService selfProxy;

    @Override public void setApplicationContext(ApplicationContext ctx) {
        this.selfProxy = ctx.getBean(MyService.class); // 取得代理
    }

    @LogAnnotation public void a() { selfProxy.b(); } // 走代理
    @LogAnnotation public void b() { ... }
}

缺點:循環依賴風險、破壞純淨性、單元測試麻煩。

3. AspectJ 編譯時織入(LTW/CTW)(徹底但複雜)

使用 `spring-aspects` + `aop.xml` + `-javaagent:aspectjweaver.jar`,直接修改位元碼,**不依賴代理**,`this.b()` 也會觸發。適合舊專案大規模切面、效能敏感場景。

4. 程式化 AOP:`AopContext.currentProxy()`(不推薦)

((MyService) AopContext.currentProxy()).b(); // 需暴露 proxy,暴力且醜陋

總結

**同類內部調用不走代理是 Spring AOP 設計使然**。首選**拆分 Bean** 解決,次選 **AspectJ 織入**,避免自我注入與 `AopContext` 等反模式。切面設計原則:切面應作用於 Bean 邊界,而非類內部。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved