Spring用AspectJ開發AOP(基於Annotation)
基於 Annotation 的宣告式
在 Spring 中,儘管使用 XML 配置檔案可以實現 AOP 開發,但是如果所有的相關的配置都集中在配置檔案中,勢必會導致 XML 配置檔案過於臃腫,從而給維護和升級帶來一定的困難。
為此,AspectJ 框架為 AOP 開發提供了另一種開發方式——基於 Annotation 的宣告式。AspectJ 允許使用註解定義切面、切入點和增強處理,而 Spring 框架則可以識別並根據這些註解生成 AOP 代理。
關於 Annotation 註解的介紹如表 1 所示。
表 1 Annotation 註解介紹
名稱 | 說明 |
---|---|
@Aspect | 用於定義一個切面。 |
@Before | 用於定義前置通知,相當於 BeforeAdvice。 |
@AfterReturning | 用於定義後置通知,相當於 AfterReturningAdvice。 |
@Around | 用於定義環繞通知,相當於MethodInterceptor。 |
@AfterThrowing | 用於定義丟擲通知,相當於ThrowAdvice。 |
@After | 用於定義最終final通知,不管是否異常,該通知都會執行。 |
@DeclareParents | 用於定義引介通知,相當於IntroductionInterceptor(不要求掌握)。 |
下面使用註解的方式重新實現《基於XML的宣告式》部分的功能。
1. 建立切面類 MyAspect
在 src 目錄下建立一個名為 com.mengma.aspectj.annotation 的包,在該包下建立一個切面類 MyAspect,如下所示。
package com.mengma.aspectj.annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; //切面類 @Aspect @Component public class MyAspect { // 用於取代:<aop:pointcut // expression="execution(*com.mengma.dao..*.*(..))" id="myPointCut"/> // 要求:方法必須是private,沒有值,名稱自定義,沒有引數 @Pointcut("execution(*com.mengma.dao..*.*(..))") private void myPointCut() { } // 前置通知 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知,目標:"); System.out.print(joinPoint.getTarget() + "方法名稱:"); System.out.println(joinPoint.getSignature().getName()); } // 後置通知 @AfterReturning(value = "myPointCut()") public void myAfterReturning(JoinPoint joinPoint) { System.out.print("後置通知,方法名稱:" + joinPoint.getSignature().getName()); } // 環繞通知 @Around("myPointCut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("環繞開始"); // 開始 Object obj = proceedingJoinPoint.proceed(); // 執行當前目標方法 System.out.println("環繞結束"); // 結束 return obj; } // 異常通知 @AfterThrowing(value = "myPointCut()",throwing = "e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("異常通知" + "出錯了" + e.getMessage()); } // 最終通知 @After("myPointCut()") public void myAfter() { System.out.println("最終通知"); } }
上述程式碼中,第 13 行 @Aspect 註解用於宣告這是一個切面類,該類作為元件使用,所以要新增 @Component 註解才能生效。第 19 行中 @Poincut 註解用於配置切入點,取代 XML 檔案中配置切入點的程式碼。
在每個通知相應的方法上都添加了註解宣告,並且將切入點方法名“myPointCut”作為引數傳遞給要執行的方法,如需其他引數(如異常通知的異常引數),可以根據程式碼提示傳遞相應的屬性值。
2. 為目標類添加註解
在 com.mengma.dao.CustomerDaoImpl 目標類中添加註解 @Repository("customerDao")。
import org.springframework.stereotype.Repository; @Repository("customerDao") public class CustomerDao { public void doSome(){ // int i=1/0; System.out.println("正式業務"); } }
3. 建立Spring配置檔案
在 com.mengma.aspectj.annotation 包下建立 applicationContext.xml 配置檔案,如下所示。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--掃描含com.mengma包下的所有註解--> <context:component-scan base-package="com.mengma"/> <!-- 使切面開啟自動代理 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
上述程式碼中,首先匯入了 AOP 名稱空間及其配套的約束,使切面類中的 @AspectJ 註解能夠正常工作;第 13 行程式碼添加了掃描包,使註解生效。需要注意的是,這裡還包括目標類 com.mengma.dao.CustomerDaoImpl 的註解,所以 base-package 的值為 com.mengma;第 15 行程式碼的作用是切面開啟自動代理。
4. 建立測試類
在 com.mengma.aspectj.annotation 包下建立一個名為 AnnotationTest 的測試類,如下所示。
package com.mengma.aspectj.annotation; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.mengma.dao.CustomerDao; public class AnnotationTest { @Test public void test() { String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext( xmlPath); // 從spring容器獲取例項 CustomerDao customerDao = (CustomerDao) applicationContext .getBean("customerDao"); // 執行方法 customerDao.add(); } }
5. 執行專案並檢視結果
使用 JUnit 測試執行 test() 方法,執行成功後,控制檯的輸出結果如圖 3 所示。
圖 3 執行結果
刪除 add() 方法中的“int i=1/0;”,重新執行 test() 方法,此時控制檯的輸出結果如圖 4 所示。
圖 4 執行結果
從圖 3 和圖 4 的輸出結果中可以看出,已成功使用 Annotation 的方式實現了 AOP 開發。與其他方式相比,基於 Annotation 方式實現 AOP 的效果是最方便的方式,所以實際開發中推薦使用註解的方式。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。