切面類定義 `@Around("@annotation(LogAnnotation)")`,Service 實現類方法加上 `@LogAnnotation`。外部呼叫 `service.a()` 觸發切面,**但 `a()` 內部呼叫 `this.b()` 時,`b()` 上的註解切面不觸發**。
@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()` → 代理 → 切面生效。符合單一職責、解耦合。
@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() { ... }
}
缺點:循環依賴風險、破壞純淨性、單元測試麻煩。
使用 `spring-aspects` + `aop.xml` + `-javaagent:aspectjweaver.jar`,直接修改位元碼,**不依賴代理**,`this.b()` 也會觸發。適合舊專案大規模切面、效能敏感場景。
((MyService) AopContext.currentProxy()).b(); // 需暴露 proxy,暴力且醜陋
**同類內部調用不走代理是 Spring AOP 設計使然**。首選**拆分 Bean** 解決,次選 **AspectJ 織入**,避免自我注入與 `AopContext` 等反模式。切面設計原則:切面應作用於 Bean 邊界,而非類內部。