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

Spring AOP 實戰精講(九):XML 配置五大通知類型與切點表達式詳解

     編輯:Java 綜合教學

Spring AOP 實戰精講(九):XML 配置五大通知類型與切點表達式詳解

AOP(面向切面編程)是 Spring 核心特性之一,本文通過完整 XML 配置範例,系統講解 5 種通知類型、切點表達式語法、代理機制原理與實戰測試。

五大通知類型對比

通知類型XML 標籤執行時機能否阻止目標方法能否獲取返回值典型場景
前置通知<aop:before>目標方法前否否權限檢查、日誌記錄、事務開啟
後置通知<aop:after-returning>目標方法正常返回後否是(returning)事務提交、結果緩存、审計日誌
最終通知<aop:after>目標方法結束時(無論正常/異常)否否資源清理、鎖釋放、性能埋點
異常通知<aop:after-throwing>目標方法拋出異常時否否(可獲取異常 throwing)異常日誌、錯誤通知、補償邏輯
環繞通知<aop:around>完全包裹目標方法是(pjp.proceed())是事務管理、緩存攔截、性能監控、重試邏輯

完整 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="
           https://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           https://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目標 Bean -->
    <bean id="personDAO" class="com.lee.spring002.aop.xml.PersonDAOImpl"/>
    <!-- 切面 Bean -->
    <bean id="transaction" class="com.lee.spring002.aop.xml.Transaction"/>

    <aop:config>
        <!-- 切點表達式:匹配 PersonDAOImpl 所有公共方法 -->
        <aop:pointcut expression="execution(* com.lee.spring002.aop.xml.PersonDAOImpl.*(..))" id="perform"/>

        <aop:aspect ref="transaction">
            <aop:before method="beginTransaction" pointcut-ref="perform"/>
            <aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>
            <aop:after method="finallyDisplay" pointcut-ref="perform"/>
            <aop:after-throwing method="exception" pointcut-ref="perform" throwing="content"/>
            <aop:around method="arround" pointcut-ref="perform"/>
        </aop:aspect>
    </aop:config>
</beans>

切點表達式語法速查


execution(public * *(..))


execution(* set*(..))


execution(* com.xyz.service.AccountService.*(..))


execution(* com.xyz.service.*.*(..))


execution(* com.xyz.service..*.*(..))

關鍵符號:* 匹配任意字符,.. 匹配任意參數列表或包層級,+ 表示類及其子類(用於類名後)。

核心代碼實現

目標接口與實現

public interface IPersonDAO {
    String savePerson();
}

public class PersonDAOImpl implements IPersonDAO {
    @Override
    public String savePerson() {
        System.out.println("PersonDAOImpl - savePerson()");
        // int a = 1 / 0; // 可取消註釋測試異常通知
        return "save successfully";
    }
}

切面類:Transaction

public class Transaction {
    // 前置通知
    public void beginTransaction(JoinPoint jp) {
        System.out.println("連接點名稱: " + jp.getSignature().getName());
        System.out.println("目標類名稱: " + jp.getTarget().getClass());
        System.out.println("Begin transaction...");
    }

    // 後置通知:獲取返回值
    public void commit(JoinPoint jp, Object val) {
        System.out.println("Transaction commit...");
        System.out.println("returning: " + val.toString());
    }

    // 最終通知
    public void finallyDisplay() {
        System.out.println("finally...");
    }

    // 異常通知:獲取異常對象
    public void exception(JoinPoint jp, Throwable content) {
        System.out.println("exception: " + content);
        System.out.println("exception msg: " + content.getMessage());
    }

    // 環繞通知:控制目標方法執行
    public void arround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("arround before...");
        pjp.proceed(); // 必須調用,否則目標方法不執行
        System.out.println("arround after...");
    }
}

單元測試與代理機制驗證

@Test
public void testPerson() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    IPersonDAO personDAO = (IPersonDAO) context.getBean("personDAO");
    personDAO.savePerson();
}

輸出結果(正常執行)

arround before...
連接點名稱: savePerson
目標類名稱: class com.lee.spring002.aop.xml.PersonDAOImpl
Begin transaction...
PersonDAOImpl - savePerson()
Transaction commit...
returning: save successfully
finally...
arround after...

Spring AOP 代理創建流程

  1. 容器啟動,實例化 personDAO、transaction 兩個 Bean
  2. 解析 <aop:config>,提取切點表達式 perform
  3. 掃描容器內 Bean,匹配切點表達式,為匹配 Bean 創建代理對象(JDK 動態代理或 CGLIB)
  4. 客戶端調用 getBean("personDAO") 時,返回代理對象而非原始 Bean
  5. 代理對象攔截方法調用,按通知順序鏈式執行:
    around(before) → before → 目標方法 → around(after) → after-returning → after

進階提示

  • 通知執行順序:同一切面多通知按 XML 聲明順序;不同切面需實現 Ordered 或加 @Order 控制優先級
  • 環繞通知必須調用 pjp.proceed(),否則目標方法不會執行;可多次調用實現重試
  • 異常通知不會阻止異常繼續拋出,若需吞噬異常請用環繞通知 try-catch
  • 返回值參數名:returning="val" 必須與通知方法參數名一致

源碼倉庫

完整 Maven 專案:https://github.com/leechenxiang/maven-spring002-aop

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