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

Spring AOP 實戰:基於 @AspectJ 註解與 Schema 配置的切面編程完整指南

     編輯:Java 綜合教學

Spring AOP 實戰:基於 @AspectJ 註解與 Schema 配置的切面編程完整指南

面向切面編程(AOP)是 Spring 核心特性之一,實現橫切關注點(事務、日誌、安全)與業務邏輯解耦。Spring 支持兩種 AOP 配置風格:@AspectJ 註解風格(推薦,類型安全、IDE 友好)與 Schema XML 風格(傳統、無侵入)。本文從 Java 註解基礎切入,深度講解兩種風格的完整用法與切點表達式。

一、Java 註解速覽:AOP 元數據基礎

JDK 5 引入註解,本質是實現 java.lang.annotation.Annotation 接口的特殊接口。關鍵元註解:

  • @Retention(RetentionPolicy.RUNTIME):運行期保留,反射可讀(AOP 必須)
  • @Target(ElementType.METHOD/TYPE/...):限定註解作用目標
  • @Documented:納入 Javadoc
  • @Inherited:子類繼承父類註解

自定義註解三步走

// 1. 定義
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
    String value() default "";
}

// 2. 使用
@LogExecution("用戶登錄")
public void login() { ... }

// 3. 反射處理
Method m = clazz.getMethod("login");
if (m.isAnnotationPresent(LogExecution.class)) {
    LogExecution ann = m.getAnnotation(LogExecution.class);
    System.out.println(ann.value());
}

二、基於 @AspectJ 註解的 AOP

需引入 spring-aop、aspectjweaver 依賴,並啟用 @EnableAspectJAutoProxy。

1. 目標類

@Component
public class GreetingService {
    public void greetTo(String name) {
        System.out.println("Hello, " + name);
    }
}

2. 切面類

@Aspect
@Component
public class LoggingAspect {
    // 前置增強:方法執行前
    @Before("execution(* greetTo(..))")
    public void beforeGreeting() {
        System.out.println("[前置] 即將問候...");
    }

    // 後置增強:方法正常返回後
    @AfterReturning(pointcut = "execution(* greetTo(..))", returning = "result")
    public void afterGreeting(Object result) {
        System.out.println("[後置] 問候完成");
    }

    // 環繞增強:完全控制方法執行(最強大,慎用)
    @Around("execution(* greetTo(..))")
    public Object aroundGreeting(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[環繞-前] 開始");
        Object ret = pjp.proceed(); // 執行目標方法
        System.out.println("[環繞-後] 結束");
        return ret;
    }

    // 異常增強:拋出異常時
    @AfterThrowing(pointcut = "execution(* greetTo(..))", throwing = "ex")
    public void onException(Exception ex) {
        System.out.println("[異常] " + ex.getMessage());
    }

    // 最終增強:finally 語義
    @After("execution(* greetTo(..))")
    public void finallyGreeting() {
        System.out.println("[最終] 清理資源");
    }
}

3. 織入方式

// 程式化織入(測試/獨立應用)
AspectJProxyFactory factory = new AspectJProxyFactory(target);
factory.addAspect(LoggingAspect.class);
GreetingService proxy = factory.getProxy();
proxy.greetTo("Spring");

三、基於 Schema (XML) 的 AOP

零侵入業務代碼,適合不想依賴 AspectJ 註解或需動態調整切面的場景。

XML 配置

<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:aop="https://www.springframework.org/schema/aop"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="...">

    <bean id="greetingService" class="com.example.GreetingService"/>
    <bean id="loggingAspect" class="com.example.LoggingAspect"/>

    <aop:config>
        <aop:aspect ref="loggingAspect">
            <aop:pointcut id="greetPointcut" expression="execution(* greetTo(..))"/>

            <aop:before method="beforeGreeting" pointcut-ref="greetPointcut"/>
            <aop:after-returning method="afterGreeting" pointcut-ref="greetPointcut" returning="result"/>
            <aop:around method="aroundGreeting" pointcut-ref="greetPointcut"/>
            <aop:after-throwing method="onException" pointcut-ref="greetPointcut" throwing="ex"/>
            <aop:after method="finallyGreeting" pointcut-ref="greetPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

四、切點表達式詳解

切點表達式定義「在何處」織入增強,execution 是最常用指示符。

execution 語法

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

常用模式示例

表達式 匹配範例
execution(* greetTo(..)) 任意返回類型、任意參數的 greetTo 方法
execution(public * *(..)) 所有 public 方法
execution(* com.service.*.*(..)) service 包下所有類的所有方法
execution(* *.*(String, ..)) 第一參數為 String 的方法
execution(@Annotation * *(..)) 帶 @Annotation 註解的方法

其他切點指示符

  • within(types):指定類型內的方法
  • this(type) / target(type):代理/目標對象類型匹配
  • args(args):參數類型匹配
  • @target(Annotation) / @annotation(Annotation):目標類/方法註解匹配
  • bean(id):Spring Bean 名稱匹配(Spring 專有)

組合邏輯

&& (and)  || (or)  ! (not)
execution(* save(..)) && within(com.service..*)

五、增強類型選擇指南

增強類型 用途 關鍵點
@Before 前置檢查、日誌、安全驗證 不能阻止方法執行(除非拋異常)
@AfterReturning 結果後處理、緩存更新 訪問返回值(returning)
@AfterThrowing 異常日誌、回滾標記 訪問異常對象(throwing)
@After 資源清理、監控上報 無論正常/異常都執行(finally)
@Around 事務管理、性能監控、緩存攔截 需手動 pjp.proceed(),性能影響最大

六、最佳實踐與避坑指南

  1. 優先使用 @AspectJ 註解:重構友好、編譯期檢查、IDE 導航支持。
  2. 切點表達式儘量精確:避免 execution(* *(..)) 這類全匹配,影響啟動性能與可讀性。
  3. 慎用 @Around:除非需要完全控制(如事務、緩存),否則優先用 @Before/@AfterReturning。
  4. @Around 必須調用 proceed():否則目標方法不執行;異常需向外拋出保持調用鏈。
  5. 避免切面依賴業務 Bean:切面應為純橫切邏輯,防止循環依賴。
  6. 啟用 expose-proxy 訪問當前代理:@EnableAspectJAutoProxy(exposeProxy = true),配合 AopContext.currentProxy() 實現代理內部方法調用攔截。

七、總結

Spring AOP 通過動態代理(JDK/CGLIB)在運行期織入切面,@AspectJ 註解與Schema XML提供同等表達能力。掌握切點表達式、增強類型語義、織入機制,能讓你優雅地實現事務、日誌、安全、監控等橫切關注點,保持業務代碼純粹專注。

自定義註解定義 註解使用示例 反射獲取註解 目標類定義 @AspectJ切面定義 AspectJProxyFactory織入 Schema XML配置

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