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

Spring AOP 完整指南:註解與 XML 兩種實現方式深度解析

     編輯:Java 綜合教學

AOP 核心概念

AOP (Aspect Oriented Programming) 是 OOP 的延續,旨在將橫切關注點(日誌、事務、安全等)與業務邏輯分離,實現解耦。本質上是動態代理機制的應用。

六大核心術語

術語定義對應配置
切面橫切關注點的模組化<aop:aspect> / @Aspect
連接點程式執行過程中的特定點(方法調用、異常拋出)隱含
通知切面在連接點執行的動作<aop:before> 等 / @Before 等
切入點匹配連接點的斷言表達式<aop:pointcut> / @Pointcut
目標對象被代理的業務對象<bean> 定義
AOP 代理Spring 生成的代理物件(JDK 動態代理 / CGLIB)proxy-target-class 屬性控制

五種通知類型

  • 前置通知 (@Before):方法執行前,不可阻止執行
  • 後置通知 (@After):方法退出時(正常/異常)
  • 返回後通知 (@AfterReturning):正常返回後
  • 環繞通知 (@Around):包圍方法執行,可控制是否執行目標方法,最強大
  • 異常通知 (@AfterThrowing):拋出異常時

方式一:註解驅動(推薦)

1. 啟用配置

<?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-3.1.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-3.0.xsd">

  <context:component-scan base-package="cn.ysh.studio.spring.aop"/>
  <aop:aspectj-autoproxy proxy-target-class="true"/>  <!-- 強制 CGLIB -->
  <bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService"/>
</beans>

2. 切面類實現

@Component
@Aspect
public class ServiceAspect {
    private static final Log log = LogFactory.getLog(ServiceAspect.class);

    @Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
    public void aspect() {}  // 空方法體,供其他通知引用

    @Before("aspect()")
    public void before(JoinPoint jp) {
        log.info("before " + jp);
    }

    @After("aspect()")
    public void after(JoinPoint jp) {
        log.info("after " + jp);
    }

    @Around("aspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();  // 執行目標方法
            log.info("around " + pjp + " 耗時:" + (System.currentTimeMillis() - start) + "ms");
            return result;
        } catch (Throwable e) {
            log.info("around " + pjp + " 異常:" + e.getMessage());
            throw e;
        }
    }

    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint jp) {
        log.info("afterReturn " + jp);
    }

    @AfterThrowing(pointcut = "aspect()", throwing = "ex")
    public void afterThrow(JoinPoint jp, Exception ex) {
        log.info("afterThrow " + jp + " " + ex.getMessage());
    }
}

3. 測試驗證

public class Tester {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = (UserService) ctx.getBean("userService");
        
        service.get(1L);
        service.save(new User());
        try { service.delete(1L); } catch (Exception e) { /* 記錄日誌 */ }
    }
}

4. 執行輸出分析

before execution(User get(long))
getUser method ...
around execution(User get(long)) 耗時:42ms
after execution(User get(long))
afterReturn execution(User get(long))

before execution(void save(User))
saveUser method ...
around execution(void save(User)) 耗時:2ms
after execution(void save(User))
afterReturn execution(void save(User))

before execution(boolean delete(long))
delete method ...
around execution(boolean delete(long)) 耗時:5ms with exception ...
after execution(boolean delete(long))
afterReturn execution(boolean delete(long))  // 注意:異常時 afterReturn 仍執行
WARN - Delete user : Null return value from advice...

方式二:XML 配置(傳統)

<beans ... xmlns:aop="https://www.springframework.org/schema/aop" ...>
  <bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>

  <aop:config>
    <aop:aspect id="simpleAspect" ref="serviceAspect">
      <aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
      
      <aop:before pointcut-ref="simplePointcut" method="before"/>
      <aop:after pointcut-ref="simplePointcut" method="after"/>
      <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
      <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
      <!-- 環繞通知需方法簽名為 Object method(ProceedingJoinPoint) -->
      <aop:around pointcut-ref="simplePointcut" method="around"/>
    </aop:aspect>
  </aop:config>
</beans>

切入點表達式詳解

execution 表達式語法

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
  • modifiers-pattern:權限修飾符(可選)
  • ret-type-pattern:返回類型(**必填**),* 表任意
  • declaring-type-pattern:類全路徑(可選),支援 .. 遞歸匹配子包
  • name-pattern:方法名(**必填**),* 表任意
  • param-pattern:參數列表,**必填**,(..) 表任意參數
  • throws-pattern:異常(可選)

常用範例

表達式含義
execution(* *(..))匹配所有方法
execution(public * com.svc.UserService.*(..))匹配 UserService 所有 public 方法
execution(* com.svc..*.*(..))匹配 com.svc 及子包所有類的所有方法

邏輯組合

  • && (and):execution(* foo()) && execution(* bar())
  • || (or):within(com.pkg1) || within(com.pkg2)
  • ! (not):!execution(* setter())

其他切入點指示符

  • within:限定類範圍
  • this:代理物件類型
  • target:目標物件類型
  • args:參數類型
  • @annotation:標註特定註解的方法

代理機制選擇

  • 目標實作介面 → 預設 JDK 動態代理
  • 目標未實作介面 → CGLIB 代理(繼承目標類)
  • 強制 CGLIB:<aop:config proxy-target-class="true"> 或 @EnableAspectJAutoProxy(proxyTargetClass = true)

掌握 AOP 核心在於理解「切入點精準匹配」與「通知類型正確選擇」,環繞通知雖強大但需謹慎使用 ProceedingJoinPoint.proceed(),否則目標方法不會執行。

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