關于AOP
面向切面編程(Aspect-oriented Programming,俗稱AOP)提供了一種面向?qū)ο缶幊?Object-oriented Programming,俗稱OOP)的補充,面向?qū)ο缶幊套詈诵牡膯卧穷?class),然而面向切面編程最核心的單元是切面(Aspects)。與面向?qū)ο蟮捻樞蛄鞒滩煌珹OP采用的是橫向切面的方式,注入與主業(yè)務流程無關的功能,例如事務管理和日志管理。
Spring的一個關鍵組件是AOP框架。 雖然Spring IoC容器不依賴于AOP(意味著你不需要在IOC中依賴AOP),但AOP為Spring IoC提供了非常強大的中間件解決方案。
AOP 是一種編程范式,最早由 AOP 聯(lián)盟的組織提出的,通過預編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術。它是 OOP的延續(xù)。利用 AOP 可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率
我們之間的開發(fā)流程都是使用順序流程,那么使用 AOP 之后,你就可以橫向抽取重復代碼,什么叫橫向抽取呢?或許下面這幅圖你能理解,先來看一下傳統(tǒng)的軟件開發(fā)存在什么樣風險。
縱向繼承體系:
在改進方案之前,我們或許都遇到過 IDEA 對你輸出 Duplicate Code 的時候,這個時候的類的設計是很糟糕的,代碼寫的也很冗余,基本上 if...el... 完成所有事情,這個時候就需要把相同的代碼抽取出來成為公共的方法,降低耦合性。這種提取代碼的方式是縱向抽取,縱向抽取的代碼之間的關聯(lián)關系非常密切。橫向抽取也是代碼提取的一種方式,不過這種方式不會修改主要業(yè)務邏輯代碼,只是在此基礎上添加一些與主要的業(yè)務邏輯無關的功能,AOP 采取橫向抽取機制,補充了傳統(tǒng)縱向繼承體系(OOP)無法解決的重復性 代碼優(yōu)化(性能監(jiān)視、事務管理、安全檢查、緩存),將業(yè)務邏輯和系統(tǒng)處理的代碼(關閉連接、事務管理、操作日志記錄)解耦。
AOP 的概念在深入學習SpringAOP 之前,讓我們先對AOP的幾個基本術語有個大致的概念,這些概念不是很容易理解,比較抽象,可以知道有這么幾個概念,下面一起來看一下:
切面(Aspect): Aspect 聲明類似于 Java 中的類聲明,事務管理是AOP一個最典型的應用。在AOP中,切面一般使用 @Aspect 注解來使用,在XML 中,可以使用 <aop:aspect> 來定義一個切面。連接點(Join Point): 一個在程序執(zhí)行期間的某一個操作,就像是執(zhí)行一個方法或者處理一個異常。在Spring AOP中,一個連接點就代表了一個方法的執(zhí)行。通知(Advice): 在切面中(類)的某個連接點(方法出)采取的動作,會有四種不同的通知方式: around(環(huán)繞通知),before(前置通知),after(后置通知), exception(異常通知),return(返回通知)。許多AOP框架(包括Spring)將建議把通知作為為攔截器,并在連接點周圍維護一系列攔截器。切入點(Pointcut):表示一組連接點,通知與切入點表達式有關,并在切入點匹配的任何連接點處運行(例如執(zhí)行具有特定名稱的方法)。由切入點表達式匹配的連接點的概念是AOP的核心,Spring默認使用AspectJ切入點表達式語言。介紹(Introduction): introduction可以為原有的對象增加新的屬性和方法。例如,你可以使用introduction使bean實現(xiàn)IsModified接口,以簡化緩存。目標對象(Target Object): 由一個或者多個切面代理的對象。也被稱為"切面對象"。由于Spring AOP是使用運行時代理實現(xiàn)的,因此該對象始終是代理對象。AOP代理(AOP proxy): 由AOP框架創(chuàng)建的對象,在Spring框架中,AOP代理對象有兩種:JDK動態(tài)代理和CGLIB代理織入(Weaving): 是指把增強應用到目標對象來創(chuàng)建新的代理對象的過程,它(例如 AspectJ 編譯器)可以在編譯時期,加載時期或者運行時期完成。與其他純Java AOP框架一樣,Spring AOP在運行時進行織入。Spring AOP 中通知的分類前置通知(Before Advice): 在目標方法被調(diào)用前調(diào)用通知功能;相關的類org.springframework.aop.MethodBeforeAdvice后置通知(After Advice): 在目標方法被調(diào)用之后調(diào)用通知功能;相關的類org.springframework.aop.AfterReturningAdvice返回通知(After-returning): 在目標方法成功執(zhí)行之后調(diào)用通知功能;異常通知(After-throwing): 在目標方法拋出異常之后調(diào)用通知功能;相關的類org.springframework.aop.ThrowsAdvice環(huán)繞通知(Around): 把整個目標方法包裹起來,在被調(diào)用前和調(diào)用之后分別調(diào)用通知功能相關的類org.aopalliance.intercept.MethodInterceptorSpring AOP 中織入的三種時期編譯期: 切面在目標類編譯時被織入,這種方式需要特殊的編譯器。AspectJ 的織入編譯器就是以這種方式織入切面的。類加載期: 切面在目標類加載到 JVM 時被織入,這種方式需要特殊的類加載器( ClassLoader ),它可以在目標類引入應用之前增強目標類的字節(jié)碼。運行期: 切面在應用運行的某個時期被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態(tài)創(chuàng)建一個代理對象,Spring AOP 采用的就是這種織入方式。AOP 的兩種實現(xiàn)方式AOP 采用了兩種實現(xiàn)方式:靜態(tài)織入(AspectJ 實現(xiàn))和動態(tài)代理(Spring AOP實現(xiàn))
AspectJAspectJ 是一個采用Java 實現(xiàn)的AOP框架,它能夠?qū)Υa進行編譯(一般在編譯期進行),讓代碼具有AspectJ 的 AOP 功能,AspectJ 是目前實現(xiàn) AOP 框架中最成熟,功能最豐富的語言。ApectJ 主要采用的是編譯期靜態(tài)織入的方式。在這個期間使用 AspectJ 的 acj 編譯器(類似 javac)把 aspect 類編譯成 class 字節(jié)碼后,在 java 目標類編譯時織入,即先編譯 aspect 類再編譯目標類。
Spring AOP 實現(xiàn)Spring AOP 是通過動態(tài)代理技術實現(xiàn)的,而動態(tài)代理是基于反射設計的。Spring AOP 采用了兩種混合的實現(xiàn)方式:JDK 動態(tài)代理和 CGLib 動態(tài)代理,分別來理解一下
JDK動態(tài)代理:Spring AOP的首選方法。 每當目標對象實現(xiàn)一個接口時,就會使用JDK動態(tài)代理。目標對象必須實現(xiàn)接口CGLIB代理:如果目標對象沒有實現(xiàn)接口,則可以使用CGLIB代理。Spring 對 AOP的支持Spring 提供了兩種AOP 的實現(xiàn):基于注解式配置和基于XML配置
@AspectJ 支持為了在Spring 配置中使用@AspectJ ,你需要啟用Spring支持,以根據(jù)@AspectJ切面配置Spring AOP,并配置自動代理。自動代理意味著,Spring 會根據(jù)自動代理為 Bean 生成代理來攔截方法的調(diào)用,并確保根據(jù)需要執(zhí)行攔截。
可以使用XML或Java樣式配置啟用@AspectJ支持。 在任何一種情況下,都還需要確保AspectJ的aspectjweaver.jar 第三方庫位于應用程序的類路徑中(版本1.8或更高版本)。
開啟@AspectJ 支持使用@Configuration 支持@AspectJ 的時候,需要添加 @EnableAspectJAutoProxy 注解,就像下面例子展示的這樣來開啟 AOP代理
@Configuration@EnableAspectJAutoProxypublic class AppConfig {}
也可以使用XML配置來開啟@AspectJ 支持
<aop:aspectj-autoproxy/>
默認你已經(jīng)添加了 aop 的schema 空間,如果沒有的話,你需要手動添加
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --></beans>聲明一個切面
在啟用了@AspectJ支持的情況下,在應用程序上下文中定義的任何bean都具有@AspectJ方面的類(具有@Aspect注釋),Spring會自動檢測并用于配置Spring AOP。
使用XML 配置的方式定義一個切面
<aop:aspect />
使用注解的方式定義一個切面
@Aspectpublic class MyAspect {}
切面(也就是用@Aspect注解的類)就像其他類一樣有屬性和方法。它們能夠包含切入點,通知和介紹聲明。
通過自動掃描檢測切面
你可以在Spring XML 配置中將切面類注冊為常規(guī)的bean,或者通過類路徑掃描自動檢測它們 - 與任何其他Spring管理的bean相同。然而,只是注解了@Aspect 的類不會被當作bean 進行管理,你還需要在類上面添加 @Component 注解,把它當作一個組件交給 Spring 管理。
定義一個切點一個切點由兩部分組成:包含名稱和任何參數(shù)以及切入點表達式的簽名,該表達式能夠確定我們想要執(zhí)行的方法。在@AspectJ注釋風格的AOP中,切入點表達式需要用@Pointcut注解標注(這個表達式作為方法的簽名,它的返回值必須是 void)。
@Pointcut("execution(* transfer(..))") // 切入點表達式private void definePointcut() {}// 方法簽名
切入點表達式的編寫規(guī)則如下:
現(xiàn)在假設我們需要配置的切點僅僅匹配指定的包,就可以使用 within() 限定符來表示,如下表達式所述:
請注意我們使用了 && 操作符把 execution() 和 within() 指示器連接在一起,表示的是 和 的關系,類似的,你還可以使用 || 操作來表示 或 的關系, 使用 ! 表示 非 的關系。
除了within() 表示的限定符外,還有其它的限定符,下面是一個限定符表
AspectJ 描述符
描述
arg()
限制連接點匹配參數(shù)為指定類型的執(zhí)行方法
@args()
限制連接點匹配參數(shù)由指定注解標注的執(zhí)行方法
execution()
用于匹配是連接點的執(zhí)行方法
this()
限制連接點匹配的AOP代理的bean引用為指定類型的類
target
限制連接點匹配目標對象為指定類型的類
@target()
限制連接點匹配特定的執(zhí)行對象,這些對象對應的類要具有指定類型的注解
within()
限制連接點匹配指定的類型
@within()
限制連接點匹配指定注解所標注的類型
@annotationn
限定匹配帶有指定注解的連接點
使用XML配置來配置切點
<aop:config> <aop:aspect ref = ""> <aop:poincut id = "" expression="execution(** com.cxuan.aop.definePointcut(......))"/> </aop:aspect></aop:config>聲明一個通知
通知是和切入點表達式相互關聯(lián),用于在方法執(zhí)行之前,之后或者方法前后,方法返回,方法拋出異常時調(diào)用通知的方法,切入點表達式可以是對命名切入點的簡單引用,也可以是在適當位置聲明的切入點表達式。下面以一個例子來演示一下這些通知都是如何定義的:
上面的例子就很清晰了,定義了一個 Audience 切面,并在切面中定義了一個performance() 的切點,下面各自定義了表演之前、表演之后返回、表演失敗的時候進行通知,除此之外,你還需要在main 方法中開啟 @EnableAspectJAutoProxy 來開啟自動代理。
除了使用Java Config 的方式外,你還可以使用基于XML的配置方式
當然,這種切點定義的比較冗余,為了解決這種類似 if...el... 災難性的業(yè)務邏輯,你需要單獨定義一個<aop:pointcut>,然后使用 pointcut-ref 屬性指向上面那個標簽,就像下面這樣
環(huán)繞通知
在目標方法執(zhí)行之前和之后都可以執(zhí)行額外代碼的通知。在環(huán)繞通知中必須顯式的調(diào)用目標方法,目標方法才會執(zhí)行,這個顯式調(diào)用時通過ProceedingJoinPoint來實現(xiàn)的,可以在環(huán)繞通知中接收一個此類型的形參,spring容器會自動將該對象傳入,注意這個參數(shù)必須處在環(huán)繞通知的第一個形參位置。
環(huán)繞通知需要返回返回值,否則真正調(diào)用者將拿不到返回值,只能得到一個null。下面是環(huán)繞通知的一個示例
<aop:around method="around" pointcut-ref="pc1"/>
public Object around(ProceedingJoinPoint jp) throws Throwable{ System.out.println("1 -- around before..."); Object obj = jp.proceed(); //--顯式的調(diào)用目標方法 System.out.println("1 -- around after..."); return obj; }
如果本文對你有幫助,別忘記給我個3連 ,點贊,轉(zhuǎn)發(fā),評論,
咱們下期見。
本文發(fā)布于:2023-02-28 20:00:00,感謝您對本站的認可!
本文鏈接:http://www.newhan.cn/zhishi/a/167764891176285.html
版權(quán)聲明:本站內(nèi)容均來自互聯(lián)網(wǎng),僅供演示用,請勿用于商業(yè)和其他非法用途。如果侵犯了您的權(quán)益請與我們聯(lián)系,我們將在24小時內(nèi)刪除。
本文word下載地址:aspectj(aspectj怎么讀).doc
本文 PDF 下載地址:aspectj(aspectj怎么讀).pdf
| 留言與評論(共有 0 條評論) |