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

Spring AOP 實現方式全景:XML 配置、@AspectJ 註解、切點函數、通知類型、零配置與最佳實踐

     編輯:Java 綜合教學

AOP 核心概念

  • 切面:橫切邏輯模塊化單元 (如事務、日志、權限)。
  • 連接點:程序執行過程中可插入切面的點 (方法調用、異常拋出)。
  • 切點:匹配連接點的謂詞表達式。
  • 通知:切面在特定切點執行的動作 (前置、後置、環繞、異常、返回)。
  • 織入:將切面應用到目標對象創建代理的過程 (編譯期/類加載期/運行期)。

方式一:XML 配置 (Spring 1.2+)

<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>

方式二:@AspectJ 註解 (Spring 2.0+,主流)

@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/>。

AspectJ 切點函數速查表

函數匹配目標示例
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() 繼續鏈路,否則目標方法不執行。

方式三:零配置 (Java Config)

@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);

代理機制選擇:JDK vs CGLIB

  • JDK 動態代理:目標實現接口 → 實現接口生成代理,proxy-target-class=false (默認)。
  • CGLIB:目標為類或強制 → 繼承目標類生成代理,proxy-target-class=true。final 方法不可代理。
  • Spring Boot 2.0+ 默認 spring.aop.proxy-target-class=true (CGLIB 優先)。

最佳實踐清單

  1. 切點表達式儘量窄化,避免誤匹配 (如 execution(* *(..)) 會匹配所有方法)。
  2. 通用切面 (事務、日志、監控) 放公共模塊,業務切面就近放置。
  3. 環繞通知謹慎使用,性能敏感路徑優先 @Before/@AfterReturning。
  4. 異常通知中不要吞噬異常,除非明確處理完畢。
  5. 單元測試驗證切面織入:AopTestUtils.getTargetObject(proxy) 獲取目標對象斷言行為。
  6. 避免在切面中注入請求作用域 Bean,導致代理創建時期循環依賴。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved