一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

2021-12-08 12:41懋為 Java教程

這篇文章主要介紹了Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

 

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用

前兩天面試的時候,面試官問了一個問題,大概意思就是一個類有兩個成員方法 A 和 B,兩者都加了事務(wù)處理注解,定義了事務(wù)傳播級別為 REQUIRE_NEW,問 A 方法內(nèi)部直接調(diào)用 B 方法時能否觸發(fā)事務(wù)處理機制。

答案有點復(fù)雜,Spring 的事務(wù)處理其實是通過AOP實現(xiàn)的,而實現(xiàn)AOP的方法有好幾種,對于通過 Jdk 和 cglib 實現(xiàn)的 aop 處理,上述問題的答案為否,對于通過AspectJ實現(xiàn)的,上述問題答案為是。

本文就結(jié)合具體例子來看一下

 

我們先定義一個接口

public interface AopActionInf {
    void doSomething_01();
    void doSomething_02();
}

 

以及此接口的一個實現(xiàn)類

public class AopActionImpl implements AopActionInf{
    public void doSomething_01() {
        System.out.println("AopActionImpl.doSomething_01()");
        //內(nèi)部調(diào)用方法 doSomething_02
        this.doSomething_02();
    }
    public void doSomething_02() {
        System.out.println("AopActionImpl.doSomething_02()");
    }
}

 

增加AOP處理

public class ActionAspectXML {
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("進入環(huán)繞通知");
        Object object = pjp.proceed();//執(zhí)行該方法
        System.out.println("退出方法");
        return object;
    }
}
<aop:aspectj-autoproxy/>
<bean id="actionImpl" class="com.maowei.learning.aop.AopActionImpl"/>
<bean id="actionAspectXML" class="com.maowei.learning.aop.ActionAspectXML"/>
<aop:config>
    <aop:aspect id = "aspectXML" ref="actionAspectXML">
        <aop:pointcut id="anyMethod" expression="execution(* com.maowei.learning.aop.AopActionImpl.*(..))"/>
        <aop:around method="aroundMethod" pointcut-ref="anyMethod"/>
    </aop:aspect>
</aop:config>

運行結(jié)果如下:

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

下圖是斷點分析在調(diào)用方法doSomething_02時的線程棧,很明顯在調(diào)用doSomething_02時并沒有對其進行AOP處理。

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

默認情況下,Spring AOP使用Jdk的動態(tài)代理機制實現(xiàn),當然也可以通過如下配置更改為cglib實現(xiàn),但是運行結(jié)果相同,此處不再贅述。

<aop:aspectj-autoproxy proxy-target-class="true"/>

那有沒有辦法能夠觸發(fā)AOP處理呢?答案是有的,考慮到AOP是通過動態(tài)生成目標對象的代理對象而實現(xiàn)的,那么只要在調(diào)用方法時改為調(diào)用代理對象的目標方法即可。

我們將調(diào)用 doSomething_02 的那行代碼改成如下,并修改相應(yīng)配置信息:

public void doSomething_01() {
    System.out.println("AopActionImpl.doSomething_01()");
    ((AopActionInf) AopContext.currentProxy()).doSomething_02();
}
<aop:aspectj-autoproxy expose-proxy="true"/>

先來看一下運行結(jié)果,

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

從運行結(jié)果可以看出,嵌套調(diào)用方法已經(jīng)能夠?qū)崿F(xiàn)AOP處理了,同樣我們看一下線程調(diào)用棧信息,顯然 doSomething_02 方法被增強處理了(紅框中內(nèi)容)。

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

 

同一對象內(nèi)的嵌套方法調(diào)用AOP失效原因分析

 

舉一個同一對象內(nèi)的嵌套方法調(diào)用攔截失效的例子

首先定義一個目標對象:

/**
 * @description: 目標對象與方法
 * @create: 2020-12-20 17:10
 */
public class TargetClassDefinition {
    public void method1(){
        method2();
        System.out.println("method1 執(zhí)行了……");
    }
    public void method2(){
        System.out.println("method2 執(zhí)行了……");
    }
}

在這個類定義中,method1()方法會調(diào)用同一對象上的method2()方法。

現(xiàn)在,我們使用Spring AOP攔截該類定義的method1()和method2()方法,比如一個簡單的性能檢測邏輯,定義如下Aspect:

/**
 * @description: 性能檢測Aspect定義
 * @create: 2020-12-20 17:13
 */
@Aspect
public class AspectDefinition {
    @Pointcut("execution(public void *.method1())")
    public void method1(){}
    @Pointcut("execution(public void *.method2())")
    public void method2(){}
    @Pointcut("method1() || method2()")
    public void pointcutCombine(){}
    @Around("pointcutCombine()")
    public Object aroundAdviceDef(ProceedingJoinPoint pjp) throws Throwable{
        StopWatch stopWatch = new StopWatch();
        try{
            stopWatch.start();
            return pjp.proceed();
        }finally {
            stopWatch.stop();
            System.out.println("PT in method [" + pjp.getSignature().getName() + "]>>>>>>"+stopWatch.toString());
        }
    }
}

由AspectDefinition定義可知,我們的Around Advice會攔截pointcutCombine()所指定的JoinPoint,即method1()或method2()的執(zhí)行。

接下來將AspectDefinition中定義的橫切邏輯織入TargetClassDefinition并運行,其代碼如下:

/**
 * @description: 啟動方法
 * @create: 2020-12-20 17:23
 */
public class StartUpDefinition {
    public static void main(String[] args) {
        AspectJProxyFactory weaver = new AspectJProxyFactory(new TargetClassDefinition());
        weaver.setProxyTargetClass(true);
        weaver.addAspect(AspectDefinition.class);
        Object proxy = weaver.getProxy();
        ((TargetClassDefinition) proxy).method1();
        System.out.println("-------------------");
        ((TargetClassDefinition) proxy).method2();
    }
}

執(zhí)行之后,得到如下結(jié)果:

method2 執(zhí)行了……
method1 執(zhí)行了……
PT in method [method1]>>>>>>StopWatch "": running time = 20855400 ns; [] took 20855400 ns = 100%
-------------------
method2 執(zhí)行了……
PT in method [method2]>>>>>>StopWatch "": running time = 71200 ns; [] took 71200 ns = 100%

不難發(fā)現(xiàn),從外部直接調(diào)用TargetClassDefinition的method2()方法的時候,因為該方法簽名匹配AspectDefinition中的Around Advice所對應(yīng)的Pointcut定義,所以Around Advice邏輯得以執(zhí)行,也就是說AspectDefinition攔截method2()成功了。但是,當調(diào)用method1()時,只有method1()方法執(zhí)行攔截成功,而method1()方法內(nèi)部的method2()方法沒有執(zhí)行卻沒有被攔截。

 

原因分析

這種結(jié)果的出現(xiàn),歸根結(jié)底是Spring AOP的實現(xiàn)機制造成的。眾所周知Spring AOP使用代理模式實現(xiàn)AOP,具體的橫切邏輯會被添加到動態(tài)生成的代理對象中,只要調(diào)用的是目標對象的代理對象上的方法,通常就可以保證目標對象上的方法執(zhí)行可以被攔截。就像TargetClassDefinition的method2()方法執(zhí)行一樣。

不過,代理模式的實現(xiàn)機制在處理方法調(diào)用的時序方面,會給使用這種機制實現(xiàn)的AOP產(chǎn)品造成一個遺憾,一般的代理對象方法與目標對象方法的調(diào)用時序如下所示:

    proxy.method2(){
        記錄方法調(diào)用開始時間;
        target.method2();
        記錄方法調(diào)用結(jié)束時間;
        計算消耗的時間并記錄到日志;
    }

在代理對象方法中,無論如何添加橫切邏輯,不管添加多少橫切邏輯,最終還是需要調(diào)用目標對象上的同一方法來執(zhí)行最初所定義的方法邏輯。

如果目標對象中原始方法調(diào)用依賴于其他對象,我們可以為目標對象注入所需依賴對象的代理,并且可以保證想用的JoinPoint被攔截并織入橫切邏輯。而一旦目標對象中的原始方法直接調(diào)用自身方法的時候,也就是說依賴于自身定義的其他方法時,就會出現(xiàn)如下圖所示問題:

Spring AOP 對象內(nèi)部方法間的嵌套調(diào)用方式

在代理對象的method1()方法執(zhí)行經(jīng)歷了層層攔截器后,最終會將調(diào)用轉(zhuǎn)向目標對象上的method1(),之后的調(diào)用流程全部都是在TargetClassDefinition中,當method1()調(diào)用method2()時,它調(diào)用的是TargetObject上的method2()而不是ProxyObject上的method2()。而針對method2()的橫切邏輯,只織入到了ProxyObject上的method2()方法中。所以,在method1()中調(diào)用的method2()沒有能夠被攔截成功。

 

解決方案

當目標對象依賴于其他對象時,我們可以通過為目標對象注入依賴對象的代理對象,來解決相應(yīng)的攔截問題。

當目標對象依賴于自身時,我們可以嘗試將目標對象的代理對象公開給它,只要讓目標對象調(diào)用自身代理對象上的相應(yīng)方法,就可以解決內(nèi)部調(diào)用的方法沒有被攔截的問題。

Spring AOP提供了AopContext來公開當前目標對象的代理對象,我們只要在目標對象中使用AopContext.currentProxy()就可以取得當前目標對象所對應(yīng)的代理對象。重構(gòu)目標對象,如下所示:

import org.springframework.aop.framework.AopContext;
/**
 * @description: 目標對象與方法
 * @create: 2020-12-20 17:10
 */
public class TargetClassDefinition {
    public void method1(){
        ((TargetClassDefinition) AopContext.currentProxy()).method2();
//        method2();
        System.out.println("method1 執(zhí)行了……");
    }
    public void method2(){
        System.out.println("method2 執(zhí)行了……");
    }
}

要使AopContext.currentProxy()生效,需要在生成目標對象的代理對象時,將ProxyConfig或者它相應(yīng)的子類的exposeProxy屬性設(shè)置為true,如下所示:

/**
 * @description: 啟動方法
 * @create: 2020-12-20 17:23
 */
public class StartUpDefinition {
    public static void main(String[] args) {
        AspectJProxyFactory weaver = new AspectJProxyFactory(new TargetClassDefinition());
        weaver.setProxyTargetClass(true);
        weaver.setExposeProxy(true);
        weaver.addAspect(AspectDefinition.class);
        Object proxy = weaver.getProxy();
        ((TargetClassDefinition) proxy).method1();
        System.out.println("-------------------");
        ((TargetClassDefinition) proxy).method2();
    }
}
<!-- 在XML文件中的開啟方式 -->
<aop:aspectj-autoproxy expose-proxy="true" />

再次執(zhí)行代碼,即可實現(xiàn)所需效果:

method2 執(zhí)行了……
PT in method [method2]>>>>>>StopWatch "": running time = 180400 ns; [] took 180400 ns = 100%
method1 執(zhí)行了……
PT in method [method1]>>>>>>StopWatch "": running time = 24027700 ns; [] took 24027700 ns = 100%
-------------------
method2 執(zhí)行了……
PT in method [method2]>>>>>>StopWatch "": running time = 64200 ns; [] took 64200 ns = 100%

后記

雖然通過將目標對象的代理對象賦給目標對象實現(xiàn)了我們的目的,但解決的方式不夠雅觀,我們的目標對象都直接綁定到了Spring AOP的具體API上了。因此,在開發(fā)中應(yīng)該盡量避免“自調(diào)用”的情況。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://blog.csdn.net/tjreal/article/details/80714294

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
主站蜘蛛池模板: 欧美久草在线 | 日本红怡院亚洲红怡院最新 | 成人18视频在线观看 | 色老妈| tube日本高清老少配 | 大胆暴露亚洲美女xxxx | 国产成人精品一区二区阿娇陈冠希 | 美女扒开屁股 | 国产美女做爰免费视频软件 | 日本不卡在线观看免费v | 成人精品第一区二区三区 | 亚洲色图150p | 亚洲九九精品 | 欧美三级免费观看 | 亚洲一区二区精品视频 | 国产-第1页-草草影院 | 亚洲国产精品无码中文字满 | cos美女被黄网站 | 99精彩免费观看 | 欧美午夜寂寞影院安卓列表 | 四虎新网站 | 91肥熟国产老肥熟在线 | 亚洲午夜大片 | 欧美xxx000喷水 | 天堂成人在线观看 | videos欧美肥婆 | 国产一区二区在线免费观看 | 国产精品林美惠子在线观看 | 忘忧草高清| 国内剧情麻豆 | 99久热只有精品视频免费观看17 | 办公室恋情在线观看 | 日产欧产va1| 成人精品一区二区三区中文字幕 | 亚洲天堂视频在线观看免费 | 校花被强迫np肉高h 校服下的白嫩小乳尖h1v1 | 大陆男同志gayxxx | aigao视频| 日本 片 成人 在线 日b视频免费 | 好大好深好涨好烫还要 | 成年视频在线观看免费 |