国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

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

服務器之家 - 編程語言 - Java教程 - Spring AOP 對象內部方法間的嵌套調用方式

Spring AOP 對象內部方法間的嵌套調用方式

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

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

 

Spring AOP 對象內部方法間的嵌套調用

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

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

本文就結合具體例子來看一下

 

我們先定義一個接口

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

 

以及此接口的一個實現類

public class AopActionImpl implements AopActionInf{
    public void doSomething_01() {
        System.out.println("AopActionImpl.doSomething_01()");
        //內部調用方法 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("進入環繞通知");
        Object object = pjp.proceed();//執行該方法
        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>

運行結果如下:

Spring AOP 對象內部方法間的嵌套調用方式

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

Spring AOP 對象內部方法間的嵌套調用方式

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

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

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

我們將調用 doSomething_02 的那行代碼改成如下,并修改相應配置信息:

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

先來看一下運行結果,

Spring AOP 對象內部方法間的嵌套調用方式

從運行結果可以看出,嵌套調用方法已經能夠實現AOP處理了,同樣我們看一下線程調用棧信息,顯然 doSomething_02 方法被增強處理了(紅框中內容)。

Spring AOP 對象內部方法間的嵌套調用方式

 

同一對象內的嵌套方法調用AOP失效原因分析

 

舉一個同一對象內的嵌套方法調用攔截失效的例子

首先定義一個目標對象:

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

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

現在,我們使用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()的執行。

接下來將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();
    }
}

執行之后,得到如下結果:

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

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

 

原因分析

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

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

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

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

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

Spring AOP 對象內部方法間的嵌套調用方式

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

 

解決方案

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

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

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

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 執行了……");
    }
    public void method2(){
        System.out.println("method2 執行了……");
    }
}

要使AopContext.currentProxy()生效,需要在生成目標對象的代理對象時,將ProxyConfig或者它相應的子類的exposeProxy屬性設置為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" />

再次執行代碼,即可實現所需效果:

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

后記

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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

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

延伸 · 閱讀

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

    小米推送Java代碼

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

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

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

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

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

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

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

    Java使用SAX解析xml的示例

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

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

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

    littleschemer13532021-05-16
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

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

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

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

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

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

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

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

    程序猿DD9332021-10-08
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 午夜爽爽爽 | 亚洲情综合五月天 | 免费观看黄视频 | 国产日韩一区二区 | 在线一区视频 | 国产一区二区三区四 | 亚洲成人高清在线 | 亚洲综合区 | 午夜男人视频 | 日本久久精品 | 精品久久ai | 一区二区三区在线免费观看 | 久久青草国产 | 亚洲视频在线免费观看 | 国产精品久久久久久久久久妞妞 | 97国产在线 | 久久亚洲一区 | 日韩一区电影 | 51国产午夜精品免费视频 | 伊人3| 日韩二区| 成人欧美一区二区三区在线播放 | 精品日韩一区二区三区 | 91精品一区二区 | 婷婷色国产偷v国产偷v小说 | 国产视频aaa | 成人亚洲一区 | 亚洲视频在线播放 | 欧美综合一区二区三区 | 九九porny88av | 美国特级a毛片免费网站 | 久久99久久99 | 日本三级韩国三级三级a级中文 | 中文字幕日韩欧美 | 91精品视频导航 | 欧美一级电影在线 | 免费一级毛片电影 | 伊人久久综合精品一区二区三区 | 免费看黄的视频网站 | 久久av网| 成人黄色电影在线观看 |