- Spring+Spring MVC+MyBatis從零開始學
- 吳為勝 楊章偉
- 2916字
- 2019-11-22 18:31:40
3.2 AspectJ開發
AspectJ是一個基于Java語言的AOP框架,它提供了強大的AOP功能。Spring 2.0以后,Spring AOP引入了對AspectJ的支持,并允許直接使用AspectJ進行編程,而Spring自身的AOP API也盡量與AspectJ保持一致。新版本的Spring框架建議使用AspectJ來開發AOP。
使用AspectJ實現AOP有兩種方式:一種是基于XML的聲明式AspectJ;另一種是基于注解的聲明式AspectJ。本節將對這兩種AspectJ的開發方式進行講解。
3.2.1 基于XML的聲明式AspectJ
基于XML的聲明式AspectJ是指通過XML文件來定義切面、切入點及通知,所有的切面、切入點和通知都必須定義在<aop:config>元素內。Spring配置文件中的<beans>元素下可以包含多個<aop:config>元素,一個<aop:config>元素中又可以包含屬性和子元素,其子元素包括<aop:pointcut>、<aop:advisor>和<aop:aspect>。在配置時,這3個子元素必須按照此順序來定義。在<aop:aspect>元素下,同樣包含屬性和多個子元素,通過使用<aop:aspect>元素及其子元素就可以在XML文件中配置切面、切入點和通知。常用元素的配置代碼如下所示。

為了讓讀者能夠清楚地掌握上述代碼中的配置信息,下面對上述代碼的配置內容進行詳細講解。
1.配置切面
在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,該元素會將一個已定義好的Spring Bean轉換成切面Bean,所以要在配置文件中先定義一個普通的Spring Bean(如上述代碼中定義的myAspect)。定義完成后,通過<aop:aspect>元素的ref屬性即可引用該Bean。
配置<aop:aspect>元素時,通常會指定id和ref兩個屬性,如表3.1所示。
表3.1 <aop:aspect>元素的屬性及其描述

2.配置切入點
在Spring的配置文件中,切入點是通過<aop:pointcut>元素來定義的。當<aop:pointcut>元素作為<aop:config>元素的子元素定義時,表示該切入點是全局切入點,可以被多個切面所共享;當<aop:pointcut>元素作為<aop:aspect>元素的子元素時,表示該切入點只對當前切面有效。在定義<aop:pointcut>元素時,通常會指定id和expression兩個屬性,如表3.2所示。
表3.2 <aop:pointcut>元素的屬性及其描述

在上述配置代碼片段中,execution(* com.ssm.jdk.*.*(..))就是定義的切入點表達式,該切入點表達式的意思是匹配com.ssm.jdk包中任意類的任意方法的執行。其中execution是表達式的主體,第1個*表示的是返回類型,使用*代表所有類型;com.ssm.jdk表示的是需要攔截的包名,后面第2個*表示的是類名,使用*代表所有的類;第3個*表示的是方法名,使用*表示所有方法;后面的()表示方法的參數,其中的“..”表示任意參數。需要注意的是,第1個*與包名之間有一個空格。
上面示例中定義的切入點表達式只是開發中常用的配置方式,而Spring AOP中切入點表達式的基本格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param- pattern) throws-pattern?
在上述格式中,各部分說明如下:
- modifiers-pattern:表示定義的目標方法的訪問修飾符,如public、private等。
- ret-type-pattern:表示定義的目標方法的返回值類型,如void、String等。
- declaring-type-pattern:表示定義的目標方法的類路徑,如com.ssm.jdk.UserDaoImpl。
- name-pattern:表示具體需要被代理的目標方法,如add()方法。
- param-pattern:表示需要被代理的目標方法包含的參數,本章示例中目標方法參數都為空。
- throws- pattern:表示需要被代理的目標方法拋出的異常類型。
提示
帶有問號(?)的部分(如modifiers-pattern、declaring-type-pattern和throws-pattern)表示可選配置項,其他部分屬于必須配置項。
想要了解更多切入點表達式的配置信息,讀者可以參考Spring官方文檔的切入點聲明部分(Declaring a pointcut)。
3.配置通知
在配置代碼中,分別使用<aop:aspect>的子元素配置了5種常用通知,這些子元素不支持再使用子元素,但在使用時可以指定一些屬性,如表3.3所示。
表3.3 通知的常用屬性及其描述

【示例3-1】了解了如何在XML中配置切面、切入點和通知后,接下來通過一個案例來演示如何在Spring中使用基于XML的聲明式AspectJ,具體實現步驟如下。
(1)創建一個名為chapter03的動態Web項目,導入Spring構架所需求的JAR包到項目的lib目錄中,并發布到類路徑下。同時,導入AspectJ框架相關的JAR包,具體如下。
- spring- aspects-4.3.6.RELEASE.jar:Spring為AspectJ提供的實現,Spring的包中已經提供。
- aspectjweaver-1.8.10.jar:是AspectJ框架所提供的規范,讀者可以通過網址“http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.10”下載。
(2)在chapter03項目的src目錄下創建一個com.ssm.aspectj包,在該包中創建接口UserDao,并在接口中編寫添加和刪除的方法,如文件3.1所示。
文件3.1 UserDao.java

(3)在com.ssm.aspectj包中創建UserDao接口的實現類UserDaoImpl,該類需要實現接口中的方法,如文件3.2所示。
文件3.2 UserDaoImpl.java

本案例中將實現類UserDaoImpl作為目標類,對其中的方法進行增強處理。
(4)在chapter03項目的src目錄下創建一個com.ssm.aspectj.xml包,在該包中創建切面類MyAspect,并在類中分別定義不同類型的通知,如文件3.3所示。
文件3.3 MyAspect.java

在文件3.3中,分別定義了5種不同類型的通知,在通知中使用了JoinPoint接口及其子接口ProceedingJoinPoint作為參數來獲得目標對象的類名、目標方法名和目標方法參數等。
注意
環繞通知必須接收一個類型為ProceedingJoinPoint的參數,返回值也必須是Object類型,且必須拋出異常。異常通知中可以傳入Throwable類型的參數來輸出異常信息。
(5)在com.ssm.aspectj.xml包中創建配置文件applicationContext.xml,并編寫相關配置,如文件3.4所示。
文件3.4 applicationContext.xml

注意
在AOP的配置信息中,使用<aop:after-returning>配置的后置通知和使用<aop:after>配置的最終通知雖然都是在目標方法執行之后執行,但它們是有區別的。后置通知只有在目標方法成功執行后才會被植入,而最終通知不論目標方法如何結束(包括成功執行和異常中止兩種情況),它都會被植入。另外,如果程序沒有異常,異常通知將不會執行。
(6)在com.ssm.aspectj.xml包下創建測試類TestXmlAspectJ,在類中為了更加清晰地演示幾種通知的執行情況,這里只對addUser()方法進行增強測試,如文件3.5所示。
文件3.5 TestXmlAspectJ.java

執行程序后,控制臺的輸出結果如圖3.1所示。

圖3.1 運行結果1
要查看異常通知的執行效果,可以在UserDaoImpl類的addUser()方法中添加出錯代碼,如“int i=10/0;”。重新運行測試類,將可以看到異常通知的執行,此時控制臺的輸出結果如圖3.2所示。

圖3.2 運行結果2
從圖3.1和圖3.2可以看出,使用基于XML的聲明式AspectJ已經實現了AOP開發。
3.2.2 基于注解的聲明式AspectJ
基于XML的聲明式AspectJ實現AOP編程雖然便捷,但是存在一些缺點,那就是要在Spring文件中配置大量的代碼信息。為了解決這個問題,AspectJ框架為AOP的實現提供了一套注解,用以取代Spring配置文件中為實現AOP功能所配置的臃腫代碼。
關于AspectJ注解的介紹如表3.4所示。
表3.4 AspectJ的注解及其描述

【示例3-2】為了使讀者可以快速地掌握這些注解,接下來重新使用注解的形式實現3.2.1小節的案例,具體步驟如下。
(1)在chapter03項目的src目錄下創建com.ssm.aspectj.annotation包,將文件3.3的切面類MyAspect復制到該包下,并對該文件進行修改,如文件3.6所示。
文件3.6 MyAspect.java


在文件3.6中,首先使用@Aspect注解定義了切面類,由于該類在Spring中是作為組件使用的,因此還需要添加@Component注解才能生效。然后使用@Pointcut注解來配置切入表達式,并通過定義方法來表示切入點名稱。接下來在每個通知相應的方法上添加了相應的注解,并將切入點名稱“myPointcut”作為參數傳遞給需要執行增強的通知方法。如果需要其他參數(如異常通知的異常參數),可以根據代碼提示傳遞相應的屬性值。
(2)在目標類com.ssm.aspectj.UserDaoImpl中添加注解@Repository("userDao")。
(3)在com.ssm.aspectj.annotation包下創建配置文件applicationContext.xml,并對該文件進行編輯,如文件3.7所示。
文件3.7 applicationContext.xml

在文件3.7中,首先引入了context約束信息,然后使用<context>元素設置了需要掃描的包,使注解生效。由于此案例中的目標類位于com.ssm.aspectj包中,因此這里設置base-package的值為“com.ssm"。最后,使用<aop.aspectj-autoproxy/>來啟動Spring對基于注解的聲明式Aspect的支持。
(4)在com.ssm.aspectj.annotation包中創建測試類TestAnnotation,該類與文件3.5基本一致,只是配置文件的路徑有所不同,如文件3.8所示。
文件3.8 TestAnnotation.java

執行程序后,控制臺的輸出結果如圖3.3所示。

圖3.3 運行結果
在UserDaoImpl類的addUser()方法中加上出錯代碼來演示異常通知的執行,控制臺的輸出結果如圖3.3所示。
從圖3.2和圖3.3可以看出,基于注解的方式與基于XML的方式執行結果相同,只是在目標方法前后通知的執行順序發生了變化。相對來說,使用注解的方式更加簡單、方便,所以在實際開發中推薦使用注解的方式進行AOP開發。
注意
如果在同一個連接點有多個通知需要執行,那么在同一切面中,目標方法之前的前置通知和環繞通知的執行順序是未知的,目標方法之后的后置通知和環繞通知的執行順序也是未知的。
- Visual C++程序設計教程
- Docker and Kubernetes for Java Developers
- Spring技術內幕:深入解析Spring架構與設計
- 碼上行動:零基礎學會Python編程(ChatGPT版)
- 區塊鏈:以太坊DApp開發實戰
- Python自然語言處理(微課版)
- Python Network Programming Cookbook(Second Edition)
- Backbone.js Blueprints
- 移動界面(Web/App)Photoshop UI設計十全大補
- Android系統原理及開發要點詳解
- Python算法詳解
- Extreme C
- Hands-On Kubernetes on Windows
- Python期貨量化交易實戰
- Visual C++程序設計與項目實踐