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

Spring AOP 程式設計完整指南:XML 與 @AspectJ 兩種配置方式實戰

     編輯:Java 相關知識

AOP 核心概念

術語說明
Aspect (切面)橫切關注點的模組化單元 (如日誌、事務、安全)
Join Point (連接點)程式執行過程中的某個點 (方法呼叫、異常拋出、欄位存取)
Pointcut (切點)匹配連接點的謂詞表達式
Advice (通知/增強)切面在特定切點執行的動作
Target Object (目標物件)被代理的物件
Weaving (織入)將切面應用到目標物件的過程 (編譯期/類載入期/運行期)

五種通知類型

類型註解XML 元素執行時機
Before@Before<aop:before>目標方法執行前
After@After<aop:after>目標方法執行後 (無論正常/異常)
After Returning@AfterReturning<aop:after-returning>目標方法正常返回後
After Throwing@AfterThrowing<aop:after-throwing>目標方法拋出異常後
Around@Around<aop:around>包圍目標方法,可控制是否執行、修改參數/返回值

方式一:XML Schema 配置 (傳統)

1. 依賴與命名空間

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

需額外引入 AspectJ JAR:aspectjrt.jar、aspectjweaver.jar、aspectj.jar

2. 切面類 (POJO)

package com.example.aop;

public class LoggingAspect {
    public void beforeAdvice() { System.out.println("Before: Setup profile"); }
    public void afterAdvice() { System.out.println("After: Profile ready"); }
    public void afterReturningAdvice(Object retVal) { 
        System.out.println("Returning: " + retVal); 
    }
    public void afterThrowingAdvice(IllegalArgumentException ex) { 
        System.out.println("Exception: " + ex); 
    }
}

3. 目標類

package com.example.aop;

public class Student {
    private String name;
    private int age;
    // getter/setter...
    public void printThrowException() { throw new IllegalArgumentException(); }
}

4. XML 配置 (beans.xml)

<aop:config>
    <aop:aspect id="logAspect" ref="logging">
        <!-- 切點:匹配 com.example.aop 包下所有類的所有方法 -->
        <aop:pointcut id="allMethods" expression="execution(* com.example.aop.*.*(..))"/>
        
        <aop:before pointcut-ref="allMethods" method="beforeAdvice"/>
        <aop:after pointcut-ref="allMethods" method="afterAdvice"/>
        <aop:after-returning pointcut-ref="allMethods" returning="retVal" method="afterReturningAdvice"/>
        <aop:after-throwing pointcut-ref="allMethods" throwing="ex" method="afterThrowingAdvice"/>
    </aop:aspect>
</aop:config>

<bean id="student" class="com.example.aop.Student">
    <property name="name" value="Alice"/>
    <property name="age" value="20"/>
</bean>
<bean id="logging" class="com.example.aop.LoggingAspect"/>

5. 切點表達式語法

execution(modifiers? return_type package.ClassName.methodName(parameters) throws?)
範例含義
execution(* com.example.*.*(..))包下所有類所有方法
execution(* com.example.Student.getName(..))指定類指定方法
execution(* com.example.service.*.*(..))service 包下所有方法
execution(* *(..))所有方法 (慎用)

方式二:@AspectJ 註解配置 (現代標準)

1. 啟用支援

<aop:aspectj-autoproxy/> <!-- XML 中聲明 -->

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {}

2. 切面類 (標註 @Aspect)

package com.example.aop;

import org.aspectj.lang.annotation.*;

@Aspect
public class LoggingAspect {
    @Pointcut("execution(* com.example.aop.*.*(..))")
    private void allMethods() {}
    
    @Before("allMethods()")
    public void beforeAdvice() { System.out.println("Before: Setup profile"); }
    
    @After("allMethods()")
    public void afterAdvice() { System.out.println("After: Profile ready"); }
    
    @AfterReturning(pointcut = "allMethods()", returning = "retVal")
    public void afterReturningAdvice(Object retVal) { 
        System.out.println("Returning: " + retVal); 
    }
    
    @AfterThrowing(pointcut = "allMethods()", throwing = "ex")
    public void afterThrowingAdvice(IllegalArgumentException ex) { 
        System.out.println("Exception: " + ex); 
    }
    
    @Around("allMethods()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Around: Before");
        Object ret = pjp.proceed(); // 執行目標方法
        System.out.println("Around: After");
        return ret;
    }
}

3. 配置極簡 (beans.xml)

<aop:aspectj-autoproxy/>
<bean id="student" class="com.example.aop.Student">...</bean>
<bean id="logging" class="com.example.aop.LoggingAspect"/>

Around 通知詳解:最強大也最複雜

@Around("execution(* com.example.service.*.*(..))")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.nanoTime();
    try {
        return pjp.proceed(); // 必須呼叫,否則目標方法不執行
    } finally {
        long elapsed = System.nanoTime() - start;
        System.out.println(pjp.getSignature() + " took " + elapsed + " ns");
    }
}
  • 可修改參數:pjp.proceed(newArgs)
  • 可捕獲/包裝異常
  • 可決定是否執行目標方法 (如快取命中直接返回)

實戰輸出對比

// XML 配置匹配所有方法
Before: Setup profile
Name: Alice
After: Profile ready
Returning: Alice
Before: Setup profile
Age: 20
After: Profile ready
Returning: 20
Before: Setup profile
Exception: java.lang.IllegalArgumentException
After: Profile ready
Exception: java.lang.IllegalArgumentException

// 修改切點僅匹配 getName()
Before: Setup profile
Name: Alice
After: Profile ready
Age: 20
Exception: java.lang.IllegalArgumentException

Spring AOP vs AspectJ 完整版

特性Spring AOPAspectJ
織入時機運行期 (代理)編譯期/類載入期
支援連接點僅方法執行欄位存取、建構子、靜態初始化等
目標物件Spring Bean任意物件
效能代理開銷無運行期開銷
複雜度低高 (需 AJC 編譯器)

最佳實踐建議

  1. 優先使用 @AspectJ 註解:類型安全、重構友善、可單元測試
  2. 切點表達式儘量精確:避免 execution(* *(..)) 全匹配,影響效能與副作用
  3. Around 謹慎使用:多數場景 Before/AfterReturning 足矣,Around 易導致目標方法不執行
  4. 通用切面獨立模組化:日誌、監控、事務、安全、快取等
  5. 避免業務邏輯滲入切面:切面應專注橫切關注點
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved