1. 程式人生 > 其它 >spring——AOP面向切面程式設計——基於註解的AspectJ AOP開發(轉載)

spring——AOP面向切面程式設計——基於註解的AspectJ AOP開發(轉載)

在 Spring 中,雖然我們可以使用 XML 配置檔案可以實現 AOP 開發,但如果所有的配置都集中在 XML 配置檔案中,就勢必會造成 XML 配置檔案過於臃腫,從而給維護和升級帶來一定困難。

為此,AspectJ 框架為 AOP 開發提供了一套 @AspectJ 註解。它允許我們直接在 Java 類中通過註解的方式對切面(Aspect)、切入點(Pointcut)和增強(Advice)進行定義,Spring 框架可以根據這些註解生成 AOP 代理。

關於註解的介紹如表 1 所示。

表 1 Annotation 註解介紹
名稱 說明
@Aspect 用於定義一個切面。
@Pointcut 用於定義一個切入點。
@Before 用於定義前置通知,相當於 BeforeAdvice。
@AfterReturning 用於定義後置通知,相當於 AfterReturningAdvice。
@Around 用於定義環繞通知,相當於 MethodInterceptor。
@AfterThrowing 用於定義丟擲通知,相當於 ThrowAdvice。
@After 用於定義最終通知,不管是否異常,該通知都會執行。
@DeclareParents 用於定義引介通知,相當於 IntroductionInterceptor(不要求掌握)。

啟用 @AspectJ 註解支援

在使用 @AspectJ 註解進行 AOP 開發前,首先我們要先啟用 @AspectJ 註解支援。

我們可以通過以下 2 種方式來啟用 @AspectJ 註解。

1)使用 Java 配置類啟用

我們可以在 Java 配置類(標註了 @Configuration 註解的類)中,使用 @EnableAspectJAutoProxy 和 @ComponentScan 註解啟用 @AspectJ 註解支援。

定義通知 

AspectJ 為我們提供了以下 6 個註解,來定義 6 種不同型別的通知(Advice),如下表。

 
註解 說明
@Before 用於定義前置通知,相當於 BeforeAdvice。
@AfterReturning 用於定義後置通知,相當於 AfterReturningAdvice。
@Around 用於定義環繞通知,相當於 MethodInterceptor。
@AfterThrowing 用於定義丟擲通知,相當於 ThrowAdvice。
@After 用於定義最終通知,不管是否異常,該通知都會執行。
@DeclareParents 用於定義引介通知,相當於 IntroductionInterceptor(不要求掌握)。


以上這些通知註解中都有一個 value 屬性,這個 value 屬性的取值就是這些通知(Advice)作用的切點(PointCut),它既可以是切入點表示式,也可以是切入點的引用(切入點對應的方法名稱),示例程式碼如下。

示例

下面,我們就通過一個完整的例項,來演示下如何通過註解的方式實現 AspectJ AOP 開發。

1. 新建一個名為 my-spring-asepctj-demo2 的 Java 專案,並將以下依賴 Jar 包匯入到該專案中。

  • commons-logging-1.2.jar
  • spring-aop-5.3.13.jar
  • spring-aspects-5.3.13.jar
  • spring-beans-5.3.13.jar
  • spring-context-5.3.13.jar
  • spring-core-5.3.13.jar
  • spring-expression-5.3.13.jar
  • aspectjweaver-1.9.7.jar


2. 在 net.biancheng.c.dao 包下,建立一個名為 UserDao 的介面,程式碼如下。


5. 在 net.biancheng.c 包下,建立一個名為 MyAspect 的切面類,程式碼如下。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package net.biancheng.c;   import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;   @Component // 定義成 Bean @Aspect //定義為切面 public class MyAspect {     @Before("execution(* net.biancheng.c.dao.UserDao.add(..))")     public void before(JoinPoint joinPoint) {         System.out.println("前置增強……" + joinPoint);     }       @After("execution(* net.biancheng.c.dao.UserDao.get(..))")     public void after(JoinPoint joinPoint) {         System.out.println("最終增強……" + joinPoint);     }         /**      * 將 net.biancheng.c.dao包下的 UserDao 類中的 get() 方法 定義為一個切點      */     @Pointcut(value = "execution(* net.biancheng.c.dao.UserDao.get(..))")     public void pointCut1() {       }       /**      * 將 net.biancheng.c.dao包下的 UserDao 類中的 delete() 方法 定義為一個切點      */     @Pointcut(value = "execution(* net.biancheng.c.dao.UserDao.delete(..))")     public void pointCut2() {       }       //使用切入點引用     @Around("MyAspect.pointCut2()")     public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {         System.out.println("環繞增強……1");         proceedingJoinPoint.proceed();         System.out.println("環繞增強……2");     }       //使用切入點表示式     @AfterReturning(value = "execution(* net.biancheng.c.dao.UserDao.modify(..))", returning = "returnValue")     public void afterReturning(Object returnValue) {         System.out.println("後置返回增強……,方法返回值為:" + returnValue);     } }