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

Spring AOP 增強類型全解析:前置/後置/異常/環繞/注解五大模式實戰

     編輯:Java 綜合教學

核心概念速覽

術語含義
切面橫切關注點模塊化(如事務、日志、權限)
連接點可被織入的方法(通常為業務接口方法)
切入點實際被織入的連接點(由切入點表達式匹配)
通知切面在特定時刻執行的增強邏輯
織入將通知應用到目標對象的過程(編譯期/類加載期/運行期)
目標對象被代理的業務對象

Spring AOP 基於動態代理:JDK 接口代理 / CGLIB 繼承代理。final 方法不可代理,故不能作為切入點。

一、XML 配置:前置/後置/異常/環繞增強

1.1 基礎組件

// 目標接口
public interface IUserBiz { void save(User user); }

// 目標實現
public class UserBiz implements IUserBiz {
    private IDao dao;
    public void save(User user) { dao.save(user); }
    // setter 注入 dao
}

1.2 四種通知實現類

// 前置通知:MethodBeforeAdvice
public class LoggerBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) {
        System.out.println("前置增強:" + method.getName());
    }
}

// 後置通知:AfterReturningAdvice
public class LoggerAfter implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
        System.out.println("後置增強,返回值:" + returnValue);
    }
}

// 異常拋出通知:ThrowsAdvice
public class ErrorLog implements ThrowsAdvice {
    public void afterThrowing(Method method, Object[] args, Object target, RuntimeException ex) {
        System.err.println("異常增強:" + method.getName() + " -> " + ex.getMessage());
    }
}

// 環繞通知:MethodInterceptor (最強大,可控制目標方法執行)
public class AroundLog implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("環繞前:" + invocation.getMethod().getName());
        Object result = invocation.proceed(); // 執行目標方法
        System.out.println("環繞後,返回:" + result);
        return result;
    }
}

1.3 Spring XML 配置

<aop:config>
    <aop:pointcut id="pc" expression="execution(public void save(cn.zhang.entity.User))"/>
    <aop:advisor advice-ref="loggerBefore" pointcut-ref="pc"/>
    <aop:advisor advice-ref="loggerAfter" pointcut-ref="pc"/>
    <aop:advisor advice-ref="errorLog" pointcut-ref="pc"/>
    <aop:advisor advice-ref="aroundLog" pointcut-ref="pc"/>
</aop:config>

執行順序:環繞前 → 前置 → 目標方法 → 後置 → 環繞後。異常時:環繞前 → 前置 → 目標方法拋異常 → 異常通知 → 環繞 catch 塊(可選重拋)。

1.4 代理工廠 Bean 替代方案

<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="biz"/>
    <property name="interceptorNames" value="loggerBefore,loggerAfter"/>
</bean>

二、注解配置:@AspectJ 風格

2.1 切面類

@Aspect
public class AnnotationAdvice {
    @Before("execution(* cn.service.UserService.*(..))")
    public void before(JoinPoint jp) {
        System.out.println("前置:" + jp.getSignature().getName() + " args=" + Arrays.toString(jp.getArgs()));
    }

    @AfterReturning(pointcut = "execution(* cn.service.UserService.*(..))", returning = "ret")
    public void after(JoinPoint jp, Object ret) {
        System.out.println("後置:" + jp.getSignature().getName() + " return=" + ret);
    }

    // 環繞通知
    @Around("execution(* cn.service.UserService.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("環繞前");
        Object ret = pjp.proceed();
        System.out.println("環繞後");
        return ret;
    }

    // 異常通知
    @AfterThrowing(pointcut = "execution(* cn.service.UserService.*(..))", throwing = "ex")
    public void afterThrow(JoinPoint jp, Exception ex) {
        System.err.println("異常:" + ex.getMessage());
    }
}

2.2 啟用註解支持

<aop:aspectj-autoproxy />

無需顧問配置,掃描 @Aspect Bean 自動代理匹配的 Bean。

2.3 JoinPoint 關鍵 API

  • getArgs():方法參數數組
  • getSignature():方法簽名(名稱、參數類型、返回類型)
  • getTarget():目標對象
  • getThis():代理對象

三、切入點表達式速查

表達式含義
execution(public * *(..))所有 public 方法
execution(* cn.service.*.*(..))service 包下所有方法
execution(* save(User))參數為 User 類型的 save 方法
@annotation(Transactional)標註 @Transactional 的方法
within(cn.service.*)service 包下所有類的方法
this(cn.service.UserService)實現 UserService 接口的代理對象方法
target(cn.service.UserServiceImpl)目標對象為 UserServiceImpl 的方法

四、最佳實踐與避坑指南

  1. 優先使用註解風格:類與切面內聚,減少 XML 維護成本。
  2. 環繞通知務必調用 proceed():否則目標方法不執行;且需返回 proceed() 結果。
  3. 切入點表達式盡量精確:避免過寬匹配導致非預期增強(如對簡單 getter 也啟動事務)。
  4. 異常通知不吞噬異常:記錄日誌後重拋,保證事務回滾機制生效。
  5. final 方法、構造方法、static 方法不可攔截:JDK 代理基於接口,CGLIB 基於繼承,均無法覆蓋 final/static。
  6. 性能考量:AOP 代理調用有微小開銷,高頻調用路徑(如循環內)避免過度切面。

五、總結

Spring AOP 提供了聲明式橫切關注點分離能力。掌握五大通知類型、切入點表達式、XML 與註解兩種配置方式、JoinPoint API,並理解代理機制與執行順序,是構建整潔、可維護企業級應用的關鍵技能。

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