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

Spring AOP 實戰:@AspectJ 註解式切面完整指南

     編輯:Java 綜合教學

Spring AOP 實戰:@AspectJ 註解式切面完整指南

AspectJ 5 引入註解式切面模型,讓任意 POJO 透過少量註解即可轉化為切面,無需學習 AspectJ 語言擴展語法。

從 XML 到註解的轉變

傳統 XML 方式需在設定檔聲明切點與通知,類別本身無切面痕跡。@AspectJ 讓切面邏輯與程式碼共存,更直觀。

核心範例:表演系統

目標物件:Singer

package com.sjf.bean;

public class Singer {
    public void perform() {
        System.out.println("正在上演個人演唱會... ");
    }
}

切面類別:Audience(觀眾)

package com.sjf.bean;

import org.aspectj.lang.annotation.*;

@Aspect
public class Audience {
    // 定義切點:匹配 Singer.perform() 任意參數
    @Pointcut("execution(* com.sjf.bean.Singer.perform(..))")
    public void singerPerform() { /* 空方法,僅供註解依附 */ }

    // 前置通知:表演前
    @Before("singerPerform()")
    public void takeSeats() {
        System.out.println("the audience is taking their seats...");
    }

    // 返回後通知:表演成功
    @AfterReturning("singerPerform()")
    public void applaud() {
        System.out.println("very good, clap clap clap...");
    }

    // 拋出異常通知:表演失敗
    @AfterThrowing("singerPerform()")
    public void demandRefund() {
        System.out.println("very bad, We want our money back...");
    }
}

關鍵註解說明

  • @Aspect:標記此類為切面
  • @Pointcut:定義可重用切點,值為 AspectJ 切點運算式,方法名即切點名
  • @Before、@AfterReturning、@AfterThrowing:通知類型,參數引用切點名
  • 切點方法體為空,僅作為註解載體

Spring 設定啟用 @AspectJ 自動代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="https://www.springframework.org/schema/context"
       xmlns:aop="https://www.springframework.org/schema/aop"
       xsi:schemaLocation="
           https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.2.xsd
           https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
           https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="singer" class="com.sjf.bean.Singer"/>
    <bean id="audience" class="com.sjf.bean.Audience"/>

    <!-- 關鍵:啟用 @AspectJ 自動代理 -->
    <aop:aspectj-autoproxy/>

</beans>

<aop:aspectj-autoproxy/> 內部建立 AnnotationAwareAspectJAutoProxyCreator,自動代理匹配切點的 Bean。

執行結果

the audience is taking their seats...
正在上演個人演唱會...
very good, clap clap clap...

XML vs @AspectJ 比較

維度XML 宣告式@AspectJ 註解式
程式碼侵入性 零侵入(POJO 完全不知情) 需標註類別與方法
原始碼需求 不需切面原始碼 需切面原始碼
可讀性 集中式配置,易管理 邏輯與程式碼共存,易理解
重用性 高(可套用至任意類別) 中(綁定特定類別)

進階用法

1. 環繞通知

@Around("singerPerform()")
public void performTime(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("the audience is taking their seats...");
    long start = System.currentTimeMillis();
    try {
        joinPoint.proceed();  // 執行目標方法
        long end = System.currentTimeMillis();
        System.out.println("very good, clap clap clap...");
        System.out.println("演出耗時 " + (end - start) + " ms");
    } catch (Throwable e) {
        System.out.println("very bad, We want our money back...");
        throw e;
    }
}

環繞通知必須接受 ProceedingJoinPoint 參數並呼叫 proceed() 執行目標方法。

2. 傳遞參數給通知

// 目標方法新增參數
public void perform(String song) { ... }

// 切面定義切點並綁定參數
@Pointcut("execution(* com.sjf.bean.Singer.perform(String)) and args(song)")
public void singerPerform() {}

@Before("singerPerform() and args(song)")
public void beforeSong(String song) {
    System.out.println("演唱歌曲為: " + song);
}

args(song) 將目標方法參數綁定至通知方法同名參數。

⚠️ 常見錯誤

// 錯誤:切點方法帶參數
@Pointcut("execution(* com.sjf.bean.Singer.perform(String)) and args(song)")
public void singerPerform(String song) { }  // ❌ 切點方法不可帶參數

// 正確:切點方法無參數,通知方法用 args 綁定
@Pointcut("execution(* com.sjf.bean.Singer.perform(String)) and args(song)")
public void singerPerform() { }

@Before("singerPerform() and args(song)")
public void beforeSong(String song) { }  // ✅

來源:《Spring 實戰》

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