chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

聊聊在使用Spring AOP時一個非常常見的概念A(yù)spectJ

OSC開源社區(qū) ? 來源:江南一點(diǎn)雨 ? 2023-08-30 09:40 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. 關(guān)于代理

小伙伴們知道,Java 23 種設(shè)計模式中有一種模式叫做代理模式,這種代理我們可以將之稱為靜態(tài)代理,Spring AOP 我們常說是一種動態(tài)代理,那么這兩種代理的區(qū)別在哪里呢?

1.1 靜態(tài)代理

這種代理在我們?nèi)粘I钪衅鋵?shí)非常常見,例如房屋中介就相當(dāng)于是一個代理,當(dāng)房東需要出租房子的時候,需要發(fā)布廣告、尋找客戶、清理房間。。。由于比較麻煩,因此房東可以將租房子這件事情委托給中間代理去做。這就是一個靜態(tài)代理。

我通過一個簡單的代碼來演示一下,首先我們有一個租房的接口,如下:

publicinterfaceRent{
voidrent();
}

房東實(shí)現(xiàn)了該接口,表示想要出租房屋:

publicclassLandlordimplementsRent{
@Override
publicvoidrent(){
System.out.println("房屋出租");
}
}

中介作為中間代理,也實(shí)現(xiàn)了該接口,同時代理了房東,如下:

publicclassHouseAgentimplementsRent{
privateLandlordlandlord;

publicHouseAgent(Landlordlandlord){
this.landlord=landlord;
}

publicHouseAgent(){
}

@Override
publicvoidrent(){
publishAd();
landlord.rent();
agencyFee();
}

publicvoidpublishAd(){
System.out.println("發(fā)布招租廣告");
}

publicvoidagencyFee(){
System.out.println("收取中介費(fèi)");
}
}

可以看到,中介的 rent 方法中,除了調(diào)用房東的 rent 方法之外,還調(diào)用了 publishAd 和 agencyFee 兩個方法。

接下來客戶租房,只需要和代理打交道就可以了,如下:

publicclassClient{
publicstaticvoidmain(String[]args){
Landlordlandlord=newLandlord();
HouseAgenthouseAgent=newHouseAgent(landlord);
houseAgent.rent();
}
}

這就是一個簡單的代理模式。無論大家是否有接觸過 Java 23 種設(shè)計模式,上面這段代碼應(yīng)該都很好理解。

這是靜態(tài)代理。

1.2 動態(tài)代理

動態(tài)代理講究在不改變原類原方法的情況下,增強(qiáng)目標(biāo)方法的功能,例如,大家平時使用的 Spring 事務(wù)功能,在不改變目標(biāo)方法的情況下,就可以通過動態(tài)代理為方法添加事務(wù)處理能力。再比如松哥在 TienChin 項(xiàng)目中所講的日志處理、接口冪等性處理、多數(shù)據(jù)源處理等,都是動態(tài)代理能力的體現(xiàn)

從實(shí)現(xiàn)原理上,我們又可以將動態(tài)代理劃分為兩大類:

編譯時增強(qiáng)。

運(yùn)行時增強(qiáng)。

1.2.1 編譯時增強(qiáng)

編譯時增強(qiáng),這種有點(diǎn)類似于 Lombok 的感覺,就是在編譯階段就直接生成了代理類,將來運(yùn)行的時候,就直接運(yùn)行這個編譯生成的代理類,AspectJ 就是這樣一種編譯時增強(qiáng)的工具。

AspectJ 全稱是 Eclipse AspectJ, 其官網(wǎng)地址是:http://www.eclipse.org/aspectj,截止到本文寫作時,目前最新版本為:1.9.7。

從官網(wǎng)我們可以看到 AspectJ 的定位:

基于 Java 語言的面向切面編程語言。

兼容 Java。

易學(xué)易用。

使用 AspectJ 時需要使用專門的編譯器 ajc。

1.2.2 運(yùn)行時增強(qiáng)

運(yùn)行時增強(qiáng)則是指借助于 JDK 動態(tài)代理或者 CGLIB 動態(tài)代理等,在內(nèi)存中臨時生成 AOP 動態(tài)代理類,我們在 Spring AOP 中常說的動態(tài)代理,一般是指這種運(yùn)行時增強(qiáng)。

我們平日開發(fā)寫的 Spring AOP,基本上都是屬于這一類。

2. AspectJ 和 Spring AOP

經(jīng)過前面的介紹,相信大家已經(jīng)明白了 AspectJ 其實(shí)也是 AOP 的一種實(shí)現(xiàn),只不過它是編譯時增強(qiáng)。

接下來,松哥再通過三個具體的案例,來和小伙伴們演示編譯時增強(qiáng)和運(yùn)行時增強(qiáng)。

2.1 AspectJ

首先,在 IDEA 中想要運(yùn)行 AspectJ,需要先安裝 AspectJ 插件,就是下面這個:

55bc36ce-465d-11ee-a2ef-92fbcf53809c.png

安裝好之后,我們需要在 IDEA 中配置一下,使用 ajc 編譯器代替 javac(這個是針對當(dāng)前項(xiàng)目的設(shè)置,所以可以放心修改):

55c3e630-465d-11ee-a2ef-92fbcf53809c.png

有如下幾個需要修改的點(diǎn):

首先修改編譯器為 ajc。

將使用的 Java 版本改為 8,這個一共有兩個地方需要修改。

設(shè)置 aspectjtools.jar 的位置,這個 jar 包需要自己提前準(zhǔn)備好,可以從 Maven 官網(wǎng)下載,然后在這里配置 jar 的路徑,配置完成之后,點(diǎn)擊 test 按鈕進(jìn)行測試,測試成功就會彈出來圖中的彈框。

對于第 3 步所需要的 jar,也可以在項(xiàng)目的 Maven 中添加如下依賴,自動下載,下載到本地倉庫之后,再刪除掉 pom.xml 中的配置即可:


org.aspectj
aspectjtools
1.9.7.M3

這樣,開發(fā)環(huán)境就準(zhǔn)備好了。

接下來,假設(shè)我有一個銀行轉(zhuǎn)帳的方法:

publicclassMoneyService{

publicvoidtransferMoney(){
System.out.println("轉(zhuǎn)賬操作");
}
}

我想給這個方法添加事務(wù),那么我就新建一個 Aspect,如下:

publicaspectTxAspect{
voidaround():call(voidMoneyService.transferMoney()){
System.out.println("開啟事務(wù)");
try{
proceed();
System.out.println("提交事務(wù)事務(wù)");
}catch(Exceptione){
System.out.println("回滾事務(wù)");
}
}
}

這就是 AspectJ 的語法,跟 Java 有點(diǎn)像,但是不太一樣。需要注意的是,這個 TxAspect 不是一個 Java 類,它的后綴是 .aj。

proceed 表示繼續(xù)執(zhí)行目標(biāo)方法,前后邏輯比較簡單,我就不多說了。

最后,我們?nèi)ミ\(yùn)行轉(zhuǎn)賬服務(wù):

publicclassDemo01{
publicstaticvoidmain(String[]args){
MoneyServicemoneyService=newMoneyService();
moneyService.transferMoney();
}
}

運(yùn)行結(jié)果如下:

55e67bbe-465d-11ee-a2ef-92fbcf53809c.png

這就是一個靜態(tài)代理。

為什么這么說呢?我們通過 IDEA 來查看一下 TxAspect 編譯之后的結(jié)果:

@Aspect
publicclassTxAspect{
static{
try{
ajc$postClinit();
}catch(Throwablevar1){
ajc$initFailureCause=var1;
}

}

publicTxAspect(){
}

@Around(
value="call(voidMoneyService.transferMoney())",
argNames="ajc$aroundClosure"
)
publicvoidajc$around$org_javaboy_demo_p2_TxAspect$1$3b99afea(AroundClosureajc$aroundClosure){
System.out.println("開啟事務(wù)");

try{
ajc$around$org_javaboy_demo_p2_TxAspect$1$3b99afeaproceed(ajc$aroundClosure);
System.out.println("提交事務(wù)事務(wù)");
}catch(Exceptionvar2){
System.out.println("回滾事務(wù)");
}

}

publicstaticTxAspectaspectOf(){
if(ajc$perSingletonInstance==null){
thrownewNoAspectBoundException("org_javaboy_demo_p2_TxAspect",ajc$initFailureCause);
}else{
returnajc$perSingletonInstance;
}
}

publicstaticbooleanhasAspect(){
returnajc$perSingletonInstance!=null;
}
}

再看一下編譯之后的啟動類:

publicclassDemo01{
publicDemo01(){
}

publicstaticvoidmain(String[]args){
MoneyServicemoneyService=newMoneyService();
transferMoney_aroundBody1$advice(moneyService,TxAspect.aspectOf(),(AroundClosure)null);
}
}

可以看到,都是修改后的內(nèi)容了。

所以說 AspectJ 的作用就有點(diǎn)類似于 Lombok,直接在編譯時期將我們的代碼改了,這就是編譯時增強(qiáng)。

2.2 Spring AOP

Spring AOP 在開發(fā)的時候,其實(shí)也使用了 AspectJ 中的注解,像我們平時使用的 @Aspect、@Around、@Pointcut 等,都是 AspectJ 里邊提供的,但是 Spring AOP 并未借鑒 AspectJ 的編譯時增強(qiáng),Spring AOP 沒有使用 AspectJ 的編譯器和織入器,Spring AOP 還是使用了運(yùn)行時增強(qiáng)。

運(yùn)行時增強(qiáng)可以利用 JDK 動態(tài)代理或者 CGLIB 動態(tài)代理來實(shí)現(xiàn)。我分別來演示。

2.2.1 JDK 動態(tài)代理

JDK 動態(tài)代理有一個要求,就是被代理的對象需要有接口,沒有接口不行,CGLIB 動態(tài)代理則無此要求。

假設(shè)我現(xiàn)在有一個計算器接口:

publicinterfaceICalculator{
intadd(inta,intb);
}

這個接口有一個實(shí)現(xiàn)類:

publicclassCalculatorImplimplementsICalculator{
@Override
publicintadd(inta,intb){
System.out.println(a+"+"+b+"="+(a+b));
returna+b;
}
}

現(xiàn)在,我想通過動態(tài)代理實(shí)現(xiàn)統(tǒng)計該接口的執(zhí)行時間功能,JDK 動態(tài)代理如下:

publicclassDemo02{
publicstaticvoidmain(String[]args){

CalculatorImplcalculator=newCalculatorImpl();
ICalculatorproxyInstance=(ICalculator)Proxy.newProxyInstance(Demo02.class.getClassLoader(),newClass[]{ICalculator.class},newInvocationHandler(){
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
longstartTime=System.currentTimeMillis();
Objectinvoke=method.invoke(calculator,args);
longendTime=System.currentTimeMillis();
System.out.println(method.getName()+"方法執(zhí)行耗時"+(endTime-startTime)+"毫秒");
returninvoke;
}
});
proxyInstance.add(3,4);
}
}

不需要任何額外依賴,都是 JDK 自帶的能力:

Proxy.newProxyInstance 方法表示要生成一個動態(tài)代理對象。

newProxyInstance 方法有三個參數(shù),第一個是一個類加載器,第二個參數(shù)是一個被代理的對象所實(shí)現(xiàn)的接口,第三個則是具體的代理邏輯。

在 InvocationHandler 中,有一個 invoke 方法,該方法有三個參數(shù),分別表示當(dāng)前代理對象,被攔截下來的方法以及方法的參數(shù),我們在該方法中可以統(tǒng)計被攔截方法的執(zhí)行時間,通過方式執(zhí)行被攔截下來的目標(biāo)方法。

最終,第一步的方法返回了一個代理對象,執(zhí)行該代理對象,就有代理的效果了。

上面這個案例就是一個 JDK 動態(tài)代理。這是一種運(yùn)行時增強(qiáng),在編譯階段并未修改我們的代碼。

2.2.2 CGLIB 動態(tài)代理

從 SpringBoot2 開始,AOP 默認(rèn)使用的動態(tài)代理就是 CGLIB 動態(tài)代理了,相比于 JDK 動態(tài)代理,CGLIB 動態(tài)代理支持代理一個類。

使用 CGLIB 動態(tài)代理,需要首先添加依賴,如下:


cglib
cglib
3.3.0

假設(shè)我有一個計算器,如下:

publicclassCalculator{
publicintadd(inta,intb){
System.out.println(a+"+"+b+"="+(a+b));
returna+b;
}
}

大家注意,這個計算器就是一個實(shí)現(xiàn)類,沒有接口。

現(xiàn)在,我想統(tǒng)計這個計算器方法的執(zhí)行時間,首先,我添加一個方法執(zhí)行的攔截器:

publicclassCalculatorInterceptorimplementsMethodInterceptor{
@Override
publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{
longstartTime=System.currentTimeMillis();
Objectresult=methodProxy.invokeSuper(o,objects);
longendTime=System.currentTimeMillis();
System.out.println(method.getName()+"方法執(zhí)行耗時"+(endTime-startTime)+"毫秒");
returnresult;
}
}

當(dāng)把代理方法攔截下來之后,額外要做的事情就在 intercept 方法中完成。通過執(zhí)行 methodProxy.invokeSuper 可以調(diào)用到代理方法。

最后,配置 CGLIB,為方法配置增強(qiáng):

publicclassDemo03{
publicstaticvoidmain(String[]args){
Enhancerenhancer=newEnhancer();
enhancer.setSuperclass(Calculator.class);
enhancer.setCallback(newCalculatorInterceptor());
Calculatorcalculator=(Calculator)enhancer.create();
calculator.add(4,5);
}
}

這里其實(shí)就是創(chuàng)建了字節(jié)增強(qiáng)器,為生成的代理對象配置 superClass,然后設(shè)置攔截下來之后的回調(diào)函數(shù)就行了,最后通過 create 方法獲取到一個代理對象。

這就是 CGLIB 動態(tài)代理。

3. 小結(jié)

經(jīng)過上面的介紹,現(xiàn)在大家應(yīng)該搞明白了靜態(tài)代理、編譯時增強(qiáng)的動態(tài)代理和運(yùn)行時增強(qiáng)的動態(tài)代理了吧~

那么我們在項(xiàng)目中到底該如何選擇呢?

先來說 AspectJ 的幾個優(yōu)勢吧。

Spring AOP 由于要生成動態(tài)代理類,因此,對于一些 static 或者 final 修飾的方法,是無法代理的,因?yàn)檫@些方法是無法被重寫的,final 修飾的類也無法被繼承。但是,AspectJ 由于不需要動態(tài)生成代理類,一切都是編譯時完成的,因此,這個問題在 AspectJ 中天然的就被解決了。

Spring AOP 有一個局限性,就是只能用到被 Spring 容器管理的 Bean 上,其他的類則無法使用,AspectJ 則無此限制(話說回來,Java 項(xiàng)目 Spring 基本上都是標(biāo)配了,所以這點(diǎn)其實(shí)到也不重要)。

Spring AOP 只能在運(yùn)行時增強(qiáng),而 AspectJ 則支持編譯時增強(qiáng),編譯后增強(qiáng)以及運(yùn)行時增強(qiáng)。

Spring AOP 支持方法的增強(qiáng),然而 AspectJ 支持方法、屬性、構(gòu)造器、靜態(tài)對象、final 類/方法等的增強(qiáng)。

AspectJ 由于是編譯時增強(qiáng),因此運(yùn)行效率也要高于 Spring AOP。

。。。

雖然 AspectJ 有這么多優(yōu)勢,但是 Spring AOP 卻有另外一個制勝法寶,那就是簡單易用

所以,我們?nèi)粘i_發(fā)中,還是 Spring AOP 使用更多。






審核編輯:劉清
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1669

    瀏覽量

    51059
  • 計算器
    +關(guān)注

    關(guān)注

    16

    文章

    440

    瀏覽量

    38693
  • JAVA語言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    21339
  • AOP
    AOP
    +關(guān)注

    關(guān)注

    0

    文章

    41

    瀏覽量

    11480

原文標(biāo)題:似懂非懂的 AspectJ

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    AOP知識詳解

    今天我們繼續(xù)看看AOP相關(guān)的知識,前面說到了Javassit,Spring AOP,通過該篇,讓你對AOP有更完整的認(rèn)識。 AOP 再看
    的頭像 發(fā)表于 09-25 11:14 ?1643次閱讀
    <b class='flag-5'>AOP</b>知識詳解

    Spring AOP如何破解java應(yīng)用

    前面我們看過javaassit是如何破解java應(yīng)用,核心都是AOP相關(guān)的知識,今天我們看下Spring AOP是怎么回事! Spring-AOP
    的頭像 發(fā)表于 09-25 11:16 ?1445次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>AOP</b>如何破解java應(yīng)用

    java spring教程

    Spring核心概念介紹控制反轉(zhuǎn)(IOC)依賴注入(DI)集合對象注入等Bean的管理BeanFactoryApplicationContextSpring web中的使用
    發(fā)表于 09-11 11:09

    什么是java spring

    或多個模塊聯(lián)合實(shí)現(xiàn)簡單來說,Spring輕量級的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的容器框架。■ 輕量——從大小與開銷兩方面而言Sprin
    發(fā)表于 09-11 11:16

    聊聊Dubbo - Dubbo可擴(kuò)展機(jī)制源碼解析

    摘要: Dubbo可擴(kuò)展機(jī)制實(shí)戰(zhàn)中,我們了解了Dubbo擴(kuò)展機(jī)制的概念,初探了Dubbo中LoadBalance的實(shí)現(xiàn),并自己實(shí)現(xiàn)了
    發(fā)表于 06-05 18:43

    Spring工作原理

    核心就是AOP這個就是面向切面編程,可以為某類對象 進(jìn)行監(jiān)督和控制(也就是調(diào)用這類對象的具體方法的前后去調(diào)用你指定的 模塊)從而達(dá)到對
    發(fā)表于 07-10 07:41

    Spring筆記分享

    Spring實(shí)現(xiàn)了使用簡單的組件配置組合成復(fù)雜的應(yīng)用。 Spring 中可以使用XML和Java注解組合這些對象。6)
    發(fā)表于 11-04 07:51

    Spring AOP使用教程及代碼詳講

    和多態(tài)性等概念來建立種對象層次結(jié)構(gòu),用以模擬公共行為的集合。當(dāng)我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關(guān)系,但并不適合定
    發(fā)表于 12-14 01:19 ?2992次閱讀

    Spring認(rèn)證是什么?

    ,例如:配置、組件掃描、AOP、數(shù)據(jù)訪問和事務(wù)、REST、安全、自動配置、執(zhí)行器、 Spring boot測試等。
    的頭像 發(fā)表于 07-04 10:19 ?1749次閱讀
    <b class='flag-5'>Spring</b>認(rèn)證是什么?

    Spring框架的簡單介紹及快速入門教程

    開發(fā)者J2EE開發(fā)中遇到的許多常見的問題,提供了功能強(qiáng)大IOC、AOP及Web MVC等功能。Spring可以單獨(dú)應(yīng)用于構(gòu)筑應(yīng)用程序,也可以和Struts、Webwork、Tapes
    的頭像 發(fā)表于 07-15 14:47 ?2346次閱讀

    剖析Spring中最常用的擴(kuò)展點(diǎn)(上)

    我們說到spring,可能第一個想到的是 `IOC`(控制反轉(zhuǎn)) 和 `AOP`(面向切面編程)。 沒錯,它們是spring的基石,
    的頭像 發(fā)表于 02-15 16:06 ?1124次閱讀
    剖析<b class='flag-5'>Spring</b>中最常用的擴(kuò)展點(diǎn)(上)

    剖析Spring中最常用的擴(kuò)展點(diǎn)(中)

    我們說到spring,可能第一個想到的是 `IOC`(控制反轉(zhuǎn)) 和 `AOP`(面向切面編程)。 沒錯,它們是spring的基石,
    的頭像 發(fā)表于 02-15 16:06 ?939次閱讀
    剖析<b class='flag-5'>Spring</b>中最常用的擴(kuò)展點(diǎn)(中)

    剖析Spring中最常用的擴(kuò)展點(diǎn)(下)

    我們說到spring,可能第一個想到的是 `IOC`(控制反轉(zhuǎn)) 和 `AOP`(面向切面編程)。 沒錯,它們是spring的基石,
    的頭像 發(fā)表于 02-15 16:07 ?881次閱讀

    解讀Spring源碼中的IOC和AOP部分

    Spring Framework 是非常流行的開源框架,為 Java 應(yīng)用程序提供了廣泛的支持和功能。
    的頭像 發(fā)表于 06-06 15:49 ?1215次閱讀

    AOP要怎么使用

    到,創(chuàng)建切面Advisor,并且將切點(diǎn)都綁定到自定義注解上面。 引入AOP的Starts: org .springframework
    的頭像 發(fā)表于 10-09 16:18 ?1287次閱讀
    <b class='flag-5'>AOP</b>要怎么使用