<aop:config proxy-target-class="true"> <!-- true=CGLIB, false=JDK 動態代理 -->
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.demo.service.*.*(..))" id="servicePointcut"/>
<aop:before method="logBefore" pointcut-ref="servicePointcut"/>
<aop:after method="logAfter" pointcut-ref="servicePointcut"/>
<aop:after-returning method="logReturn" pointcut-ref="servicePointcut" returning="result"/>
<aop:after-throwing method="logException" pointcut-ref="servicePointcut" throwing="ex"/>
<aop:around method="profileAround" pointcut-ref="servicePointcut"/>
</aop:aspect>
</aop:config>
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.demo.service.*.*(..))")
public void servicePointcut() {}
@Before("servicePointcut()")
public void logBefore(JoinPoint jp) { /* ... */ }
@After("servicePointcut()")
public void logAfter(JoinPoint jp) { /* ... */ }
@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void logReturn(JoinPoint jp, Object result) { /* ... */ }
@AfterThrowing(pointcut = "servicePointcut()", throwing = "ex")
public void logException(JoinPoint jp, Exception ex) { /* ... */ }
@Around("servicePointcut()")
public Object profileAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
Object result = pjp.proceed();
log.info("{} took {} ms", pjp.getSignature(), (System.nanoTime()-start)/1e6);
return result;
}
}
啟用:@EnableAspectJAutoProxy(proxyTargetClass = true) 或 XML <aop:aspectj-autoproxy/>。
| 函數 | 匹配目標 | 示例 |
|---|---|---|
| execution | 方法簽名 | execution(public * *(..)) |
| within | 類/包範圍 | within(com.demo.service..*) |
| this | 代理對象類型 | this(com.demo.api.Service) |
| target | 目標對象類型 | target(com.demo.impl.ServiceImpl) |
| args | 運行時參數類型 | args(String, int) |
| @annotation | 方法註解 | @annotation(com.demo.Loggable) |
| @within | 類註解 | @within(org.springframework.stereotype.Service) |
| @target | 目標類註解 | @target(org.springframework.stereotype.Repository) |
| @args | 參數註解 | @args(com.demo.Valid) |
execution 完整語法:execution(modifiers? return_type declaring_type.name(param) throws?)。
| 通知 | 註解 | 執行時機 | 典型用途 | 參數 |
|---|---|---|---|---|
| 前置 | @Before | 方法調用前 | 權限檢查、參數驗證 | JoinPoint |
| 後置(最終) | @After | 方法退出時 (正常/異常) | 資源清理、日志結束 | JoinPoint |
| 返回後 | @AfterReturning | 正常返回後 | 結果處理、緩存更新 | JoinPoint, returning |
| 異常後 | @AfterThrowing | 拋出異常後 | 異常日志、事務回滾標記 | JoinPoint, throwing |
| 環繞 | @Around | 完全控制調用 | 性能監控、事務管理、重試、緩存 | ProceedingJoinPoint |
環繞通知必須調用 pjp.proceed() 繼續鏈路,否則目標方法不執行。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.demo")
public class AopConfig {
@Bean public UserService userService() { return new UserServiceImpl(); }
@Bean public LoggingAspect loggingAspect() { return new LoggingAspect(); }
}
// 啟動
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
proxy-target-class=false (默認)。proxy-target-class=true。final 方法不可代理。spring.aop.proxy-target-class=true (CGLIB 優先)。execution(* *(..)) 會匹配所有方法)。AopTestUtils.getTargetObject(proxy) 獲取目標對象斷言行為。