spring——AOP面向切面程式設計——基於註解的AspectJ AOP開發(轉載)
在 Spring 中,雖然我們可以使用 XML 配置檔案可以實現 AOP 開發,但如果所有的配置都集中在 XML 配置檔案中,就勢必會造成 XML 配置檔案過於臃腫,從而給維護和升級帶來一定困難。
為此,AspectJ 框架為 AOP 開發提供了一套 @AspectJ 註解。它允許我們直接在 Java 類中通過註解的方式對切面(Aspect)、切入點(Pointcut)和增強(Advice)進行定義,Spring 框架可以根據這些註解生成 AOP 代理。
關於註解的介紹如表 1 所示。
名稱 | 說明 |
---|---|
@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);
}
}
|