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

Spring AOP 實戰:基於 @AspectJ 註解的切面開發完全指南

     編輯:Java 相關知識

@AspectJ 支援的五種通知類型

註解類型執行時機典型用途
@Before前置通知方法執行之前權限檢查、事務開啟、日誌記錄
@After後置通知方法執行之後(無論正常/異常)資源清理、事務提交/回滾
@AfterReturning返回通知方法正常返回後返回值處理、緩存更新、審計日誌
@AfterThrowing異常通知方法拋出異常後異常轉換、錯誤通知、補償操作
@Around環繞通知完全控制方法執行流程性能監控、重試機制、事務邊界控制

核心配置步驟

1. 依賴與 Schema

  • 引入 aspectjrt.jar、aspectjweaver.jar
  • Spring 配置文件添加 aop namespace:xmlns:aop="https://www.springframework.org/schema/aop"
  • 啟用自動代理:<aop:aspectj-autoproxy/>

2. 切面類編寫

package www.csdn.spring.advice.aspetjs;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AdviceImpl {

    // 前置通知:匹配 UserDaoImpl 所有方法
    @Before("execution(* UserDaoImpl.*(..))")
    public void doTransAction() {
        System.out.println("----開啟事務-----");
    }

    // 後置通知:匹配 www.csdn 包下 UserDaoImpl 所有方法
    @After("execution(* www.csdn..UserDaoImpl.*(..))")
    public void doAfterTransAction() {
        System.out.println("-------提交事務-------");
    }

    // 環繞通知:僅匹配 save 方法,演示 ProceedingJoinPoint 用法
    @Around("execution(* www.csdn..UserDaoImpl.save(..))")
    public void doSec(ProceedingJoinPoint jp) {
        System.out.println("----------安全處理之前-------------");
        try {
            Object obj = jp.proceed(); // 執行目標方法
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("----------安全處理之後-------------");
    }
}

3. Spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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="adviceImpl" class="www.csdn.spring.advice.aspetjs.AdviceImpl"/>

    <!-- 目標 Bean -->
    <bean id="userDaoImpl" class="www.csdn.spring.advice.aspetjs.UserDaoImpl"/>

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

</beans>

4. 測試驗證

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-asp*.xml");
    UserDao uDao = (UserDao) context.getBean("userDaoImpl");
    uDao.save(null);
    // uDao.delete(null);
    // uDao.update(null);
    // uDao.getObjects();
}

進階特性詳解

1. 獲取方法簽名與參數

@Before("execution(* com.service..*.*(..))")
public void logBefore(JoinPoint jp) {
    Signature sig = jp.getSignature(); // 方法簽名
    String methodName = sig.getName();
    Object[] args = jp.getArgs(); // 參數數組
    System.out.println("調用: " + methodName + ", 參數: " + Arrays.toString(args));
}

2. @AfterReturning 獲取返回值

@AfterReturning(pointcut = "execution(* com.service..*.*(..))", returning = "result")
public void logAfterReturn(JoinPoint jp, Object result) {
    System.out.println("返回值: " + result);
}

3. @AfterThrowing 獲取異常

@AfterThrowing(pointcut = "execution(* com.service..*.*(..))", throwing = "ex")
public void logException(JoinPoint jp, Exception ex) {
    System.err.println("異常: " + ex.getMessage());
}

4. @Around 完全控制執行流程

@Around("execution(* com.service..*.*(..))")
public Object around(ProceedingJoinPoint jp) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = jp.proceed(); // 繼續執行目標方法
    long elapsed = System.currentTimeMillis() - start;
    System.out.println("耗時: " + elapsed + "ms");
    return result; // 必須返回結果
}

警示:環繞通知必須調用 jp.proceed() 且返回其結果,否則目標方法不執行或調用方拿不到返回值。

5. 切面優先級控制

@Aspect
@Order(0) // 值越小,優先級越高(最先執行)
public class SecurityAspect { ... }

@Aspect
@Order(1)
public class TransactionAspect { ... }

切點表達式復用最佳實踐

@Aspect
public class CommonPointcuts {

    @Pointcut("execution(* com.example.service..*.*(..))")
    public void serviceLayer() {}

    @Pointcut("execution(* com.example.dao..*.*(..))")
    public void dataAccessLayer() {}

    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
}

// 其他切面引用
@Before("CommonPointcuts.serviceLayer()")
public void doSomething() { ... }

常見問題與避坑

  • 切面不生效:確認 <aop:aspectj-autoproxy/> 已配置、切面 Bean 被容器掃描(@Component 或 XML 定義)
  • 目標方法非 public:Spring AOP 基於代理,僅攔截 public 方法
  • 自調用問題:類內部方法調用不會觸發代理,需注入自身或重構
  • 參數綁定失敗:returning/throwing 參數名必須與方法參數名一致
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved