Spring AOP、AspectJ、CGLIB 怎么說清楚?
點(diǎn)擊下方“IT牧場”,選擇“設(shè)為星標(biāo)”

來源?|?www.jianshu.com/p/fe8d1e8bd63e
01、先說說AspectJ 02、再談 Spring?AOP 03、Hello World后的總結(jié) 04、最后說說CGLIB
AOP(Aspect Orient Programming),作為面向?qū)ο缶幊痰囊环N補(bǔ)充,廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級服務(wù),如事務(wù)管理、安全檢查、緩存、對象池管理等。AOP 實(shí)現(xiàn)的關(guān)鍵就在于 AOP 框架自動創(chuàng)建的 AOP 代理,AOP 代理則可分為靜態(tài)代理和動態(tài)代理兩大類,其中靜態(tài)代理是指使用 AOP 框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時(shí)增強(qiáng);而動態(tài)代理則在運(yùn)行時(shí)借助于 JDK 動態(tài)代理、CGLIB 等在內(nèi)存中“臨時(shí)”生成 AOP 動態(tài)代理類,因此也被稱為運(yùn)行時(shí)增強(qiáng)。
01、先說說AspectJ
在今天之前,我還以為AspectJ 是Spring的一部分,因?yàn)槲覀冋劦絊pring?AOP一般都會提到AspectJ。原來AspectJ是一套獨(dú)立的面向切面編程的解決方案。下面我們拋開Spring,單純的看看AspectJ。
AspectJ 安裝 AspectJ 下載地址(http://www.eclipse.org/aspectj/downloads.php)。 下載AspectJ ?jar包,然后雙擊安裝。安裝好的目錄結(jié)構(gòu)為: bin: 存放了 aj、aj5、ajc、ajdoc、ajbrowser 等命令,其中 ajc 命令最常用,它的作用類似于 javacdoc:存放了 AspectJ 的使用說明、參考手冊、API 文檔等文檔lib:該路徑下的 4 個(gè) JAR 文件是 AspectJ 的核心類庫AspectJ HelloWorld 實(shí)現(xiàn)
業(yè)務(wù)組件??SayHelloService
package?com.ywsc.fenfenzhong.aspectj.learn;
public?class?SayHelloService?{
????public?void?say(){
????????System.out.print("Hello??AspectJ");
????}
}?
需要來了,在需要在調(diào)用say()方法之后,需要記錄日志。那就是通過AspectJ的后置增強(qiáng)吧。?
LogAspect?日志記錄組件,實(shí)現(xiàn)對com.ywsc.fenfenzhong.aspectj.learn.SayHelloService?后置增強(qiáng)
package?com.ywsc.fenfenzhong.aspectj.learn;
public?aspect?LogAspect{
????pointcut?logPointcut():execution(void?SayHelloService.say());
????after():logPointcut(){
?????????System.out.println("記錄日志?...");?
????}
}
編譯SayHelloService
執(zhí)行命令???ajc?-d?.?SayHelloService.java?LogAspect.java
生成?SayHelloService.class
執(zhí)行命令????java?SayHelloService
輸出??Hello?AspectJ??記錄日志
ajc.exe 可以理解為 javac.exe 命令,都用于編譯 Java 程序,區(qū)別是 ajc.exe 命令可識別 AspectJ 的語法;我們可以將 ajc.exe 當(dāng)成一個(gè)增強(qiáng)版的 javac.exe 命令.執(zhí)行ajc命令后的 SayHelloService.class 文件不是由原來的 SayHelloService.java 文件編譯得到的,該 SayHelloService.class 里新增了打印日志的內(nèi)容——這表明 AspectJ 在編譯時(shí)“自動”編譯得到了一個(gè)新類,這個(gè)新類增強(qiáng)了原有的 SayHelloService.java 類的功能,因此 AspectJ 通常被稱為編譯時(shí)增強(qiáng)的?AOP?框架。` 與 AspectJ 相對的還有另外一種?AOP?框架,它不需要在編譯時(shí)對目標(biāo)類進(jìn)行增強(qiáng),而是運(yùn)行時(shí)生成目標(biāo)類的代理類,該代理類要么與目標(biāo)類實(shí)現(xiàn)相同的接口,要么是目標(biāo)類的子類——總之,代理類的實(shí)例可作為目標(biāo)類的實(shí)例來使用。一般來說,編譯時(shí)增強(qiáng)的 AOP 框架在性能上更有優(yōu)勢——因?yàn)檫\(yùn)行時(shí)動態(tài)增強(qiáng)的 AOP 框架需要每次運(yùn)行時(shí)都進(jìn)行動態(tài)增強(qiáng)。
02、再談 Spring?AOP
Spring?AOP也是對目標(biāo)類增強(qiáng),生成代理類。但是與AspectJ的最大區(qū)別在于---Spring AOP的運(yùn)行時(shí)增強(qiáng),而AspectJ是編譯時(shí)增強(qiáng)。曾經(jīng)以為AspectJ是Spring AOP一部分,是因?yàn)镾pring AOP使用了AspectJ的Annotation。使用了Aspect來定義切面,使用Pointcut來定義切入點(diǎn),使用Advice來定義增強(qiáng)處理。雖然使用了Aspect的Annotation,但是并沒有使用它的編譯器和織入器。其實(shí)現(xiàn)原理是JDK 動態(tài)代理,在運(yùn)行時(shí)生成代理類。為了啟用 Spring 對 @AspectJ 方面配置的支持,并保證 Spring 容器中的目標(biāo) Bean 被一個(gè)或多個(gè)方面自動增強(qiáng),必須在 Spring 配置文件中添加如下配置
???<aop:aspectj-autoproxy/>
當(dāng)啟動了 @AspectJ 支持后,在 Spring 容器中配置一個(gè)帶 @Aspect 注釋的 Bean,Spring 將會自動識別該 Bean,并將該 Bean 作為方面 Bean 處理。方面Bean與普通 Bean 沒有任何區(qū)別,一樣使用
業(yè)務(wù)組件??SayHelloService
package?com.ywsc.fenfenzhong.aspectj.learn;
import?org.springframework.stereotype.Component;
@Component
public?class?SayHelloService?{
????public?void?say(){
????????System.out.print("Hello??AspectJ");
????}
}?
做后置增強(qiáng)的日志處理。
package?com.ywsc.fenfenzhong.aspectj.learn;
import?org.aspectj.lang.annotation.After;
import?org.aspectj.lang.annotation.Aspect;
import?org.springframework.stereotype.Component;
@Aspect
@Component
public?class?LogAspect?{
?????@After("execution(?com.ywsc.fenfenzhong.aspectj.learn.SayHelloService.(..))")
?????public?void?log(){
?????????System.out.println("記錄日志?...");
?????}
}
package?com.ywsc.fenfenzhong.mongodb;
import?com.ywsc.fenfenzhong.aspectj.learn.SayHelloService;
public?class?TestCase?{
????public?static?void?main(String[]?args)?{
????????SayHelloService?sayHelloService?=?ApplicationUtil.getContext().getBean(SayHelloService.class);
????????sayHelloService.say();
????}
}
輸出結(jié)果
Hello??AspectJ
記錄日志...
03、Hello World后的總結(jié)
AOP?代理 = 原來的業(yè)務(wù)類+增強(qiáng)處理。這個(gè)生成AOP?代理由 Spring 的 IoC 容器負(fù)責(zé)生成。也由 IoC 容器負(fù)責(zé)管理。因此,AOP 代理可以直接使用容器中的其他 Bean 實(shí)例作為目標(biāo),這種關(guān)系可由 IoC 容器的依賴注入提供?;仡橦ello World的例子,其中程序員參與的只有 3 個(gè)部分:. 定義普通業(yè)務(wù)組件。. 定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件。. 定義增強(qiáng)處理,增強(qiáng)處理就是在?AOP?框架為普通業(yè)務(wù)組件織入的處理動作。
04、最后說說CGLIB
CGLIB(Code Generation Library)它是一個(gè)代碼生成類庫。它可以在運(yùn)行時(shí)候動態(tài)是生成某個(gè)類的子類。代理模式為要訪問的目標(biāo)對象提供了一種途徑,當(dāng)訪問對象時(shí),它引入了一個(gè)間接的層。JDK自從1.3版本開始,就引入了動態(tài)代理,并且經(jīng)常被用來動態(tài)地創(chuàng)建代理。JDK的動態(tài)代理用起來非常簡單,唯一限制便是使用動態(tài)代理的對象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口。而CGLIB缺不必有此限制。要想Spring AOP?通過CGLIB生成代理,只需要在Spring 的配置文件引入
<aop:aspectj-autoproxy?proxy-target-class="true"/>
CGLIB包的底層是通過使用一個(gè)小而快的字節(jié)碼處理框架ASM(Java字節(jié)碼操控框架),來轉(zhuǎn)換字節(jié)碼并生成新的類。由于沒有了解過class 文件和字節(jié)碼,因而也就寫不下去了。也許學(xué)習(xí)下來最大的收獲便是 弄清楚了 AspectJ 和 Spring AOP?在實(shí)現(xiàn)上幾乎無關(guān)。
干貨分享
最近將個(gè)人學(xué)習(xí)筆記整理成冊,使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤地址,無套路領(lǐng)?。?/p>
?001:《Java并發(fā)與高并發(fā)解決方案》學(xué)習(xí)筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學(xué)習(xí)筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領(lǐng)域驅(qū)動設(shè)計(jì)速成)》?007:全部?008:加技術(shù)群討論
加個(gè)關(guān)注不迷路
喜歡就點(diǎn)個(gè)"在看"唄^_^
