最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

分析java 中AspectJ切面執(zhí)行兩次的原因

 更新時間:2017年09月05日 10:56:13   投稿:lqh  
這篇文章主要介紹了分析java 中AspectJ切面執(zhí)行兩次的原因的相關(guān)資料,希望通過本能幫助到大家,需要的朋友可以參考下

分析java 中AspectJ切面執(zhí)行兩次的原因

背景

轉(zhuǎn)眼之間,發(fā)現(xiàn)博客已經(jīng)將近半年沒更新了,甚是慚愧。話不多說,正如標題所言,最近在使用AspectJ的時候,發(fā)現(xiàn)攔截器(AOP切面)執(zhí)行了兩次了。我們知道,AspectJ是AOP的一種解決方案,本質(zhì)上是通過代理類在目標方法執(zhí)行通知(Advice),然后由代理類再去調(diào)用目標方法。所以,從這點講,攔截器應(yīng)該只會執(zhí)行一次。但是在測試的時候發(fā)現(xiàn)攔截器執(zhí)行了兩次。

問題重現(xiàn)

既然問題已經(jīng)明了,那么可以通過代碼簡單重現(xiàn)這個問題,從而更深層次分析到底是什么原因?qū)е碌摹?/p>

定義一個注解:

package com.rhwayfun.aspect;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface StatsService {
}

為該注解定義切面:

package com.rhwayfun.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class StatsServiceInterceptor {

  private static Logger log = LoggerFactory.getLogger(StatsServiceInterceptor.class);

  @Around("@annotation(StatsService)")
  public Object invoke(ProceedingJoinPoint pjp) {
    try {
      log.info("before invoke target.");
      return pjp.proceed();
    } catch (Throwable e) {
      log.error("invoke occurs error:", e);
      return null;
    } finally {
      log.info("after invoke target.");
    }
  }

}

方法測試:

package com.rhwayfun;

import com.rhwayfun.aspect.StatsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;

public class AspectTest {

  private static Logger log = LoggerFactory.getLogger(AspectTest.class);

  public static void main(String[] args) {
    AspectTest.print();
  }

  @StatsService
  public static void print(){
    log.info("Now: {}", LocalDateTime.now());
  }
}

輸出結(jié)果:

debug分析

由于是靜態(tài)織入,所以可以通過反編譯工具查看編譯后的文件,如下:

public class AspectTest
{
  private static Logger log;
  private static final /* synthetic */ JoinPoint$StaticPart ajc$tjp_0;
  private static final /* synthetic */ JoinPoint$StaticPart ajc$tjp_1;

  public static void main(final String[] args) {
    StatsServiceInterceptor.aspectOf().invoke(((AroundClosure)new AspectTest$AjcClosure1(new Object[] { Factory.makeJP(AspectTest.ajc$tjp_0, (Object)null, (Object)null) })).linkClosureAndJoinPoint(0));
  }

  @StatsService
  public static void print() {
    StatsServiceInterceptor.aspectOf().invoke(((AroundClosure)new AspectTest$AjcClosure3(new Object[] { Factory.makeJP(AspectTest.ajc$tjp_1, (Object)null, (Object)null) })).linkClosureAndJoinPoint(65536));
  }

  static {
    ajc$preClinit();
    AspectTest.log = LoggerFactory.getLogger((Class)AspectTest.class);
  }

  private static /* synthetic */ void ajc$preClinit() {
    final Factory factory = new Factory("AspectTest.java", (Class)AspectTest.class);
    ajc$tjp_0 = factory.makeSJP("method-call", (Signature)factory.makeMethodSig("9", "print", "com.rhwayfun.AspectTest", "", "", "", "void"), 17);
    ajc$tjp_1 = factory.makeSJP("method-execution", (Signature)factory.makeMethodSig("9", "print", "com.rhwayfun.AspectTest", "", "", "", "void"), 22);
  }
}

請注意兩個連接點:ajc$tjp_0和ajc$tjp_1,這兩個連接點是產(chǎn)生兩次調(diào)用的關(guān)鍵,問題注解明明是加上print()方法上的,為什么main()方法也被注入了通知呢?正因為main()方法也織入了通知,所以就形成了A call B, B call print()的調(diào)用鏈,有兩次method-call,一次method-execution,method-execution才是我們的目標方法print(),所以我們才看到了兩次輸出。

method-call和method-execution都是連接點ProceedingJoinPoint的kind屬性

其實,這屬于Ajc編譯器的一個Bug,詳見Ajc-bug

所以,到這一步,問題就很清晰了,因為Ajc編輯器的bug,導(dǎo)致了在main方法中也織入了通知,所以在執(zhí)行的時候,輸出了兩次日志。

解決方法

方案一

因為兩次調(diào)用的kind屬性不一樣,所以可以通過kind屬性來判斷時候調(diào)用切面。這樣顯得不優(yōu)雅,而且如果切面有更多的邏輯的話,需要加各種if-else的判斷,所以不推薦。

方法二

更優(yōu)雅的方案是修改@Around("@annotation(StatsService)")的邏輯,改為@Around("execution(* *(..)) && @annotation(StatsService)")。

重新運行上面的測試類,結(jié)果如下:

如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn)

    springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn)

    本文主要介紹了springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java使用正則表達式驗證用戶名和密碼的方法

    Java使用正則表達式驗證用戶名和密碼的方法

    這篇文章主要介紹了Java使用正則表達式驗證用戶名和密碼的方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • SpringBoot項目如何打包部署到服務(wù)器

    SpringBoot項目如何打包部署到服務(wù)器

    這篇文章主要介紹了SpringBoot項目如何打包部署到服務(wù)器問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Springboot初始化啟動報錯Error?creating?bean?with?name?'dataSource'?defined?in?class?path?resource

    Springboot初始化啟動報錯Error?creating?bean?with?name?'da

    這篇文章主要為大家介紹了Springboot初始化啟動報Error?creating?bean?with?name?'dataSource'?defined?in?class?path?resource解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • java安全之CommonsCollections4詳解

    java安全之CommonsCollections4詳解

    這篇文章主要介紹了java安全之CommonsCollections4詳解
    2022-08-08
  • Spring?Boot項目傳參校驗的最佳實踐指南

    Spring?Boot項目傳參校驗的最佳實踐指南

    有參數(shù)傳遞的地方都少不了參數(shù)校驗,在web開發(fā)中前端的參數(shù)校驗是為了用戶體驗,后端的參數(shù)校驗是為了安全,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot項目傳參校驗的最佳實踐,需要的朋友可以參考下
    2022-04-04
  • JVM Client和Server端有什么區(qū)別

    JVM Client和Server端有什么區(qū)別

    這篇文章主要介紹了JVM Client和Server端有什么區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友可以參考下
    2020-11-11
  • 簡述Mybatis增刪改查實例代碼

    簡述Mybatis增刪改查實例代碼

    本文給大家分享編寫一個簡單的mybatis進行插入數(shù)據(jù)的實例代碼,非常不錯具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-10-10
  • SpringBoot中優(yōu)化if-else語句的七種方法

    SpringBoot中優(yōu)化if-else語句的七種方法

    if-else語句是控制流程的基本工具,但過度使用會使代碼變得復(fù)雜且難以維護,在SpringBoot , SpringCloud項目中,優(yōu)化if-else結(jié)構(gòu)變得尤為重要,本文將深入探討七種策略,旨在減少SpringBoot , SpringCloud項目中 if-else的使用,需要的朋友可以參考下
    2024-07-07
  • java中this關(guān)鍵字的詳細使用介紹

    java中this關(guān)鍵字的詳細使用介紹

    大家好,本篇文章主要講的是java中this關(guān)鍵字的詳細使用介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01

最新評論

长武县| 延吉市| 长岛县| 新昌县| 洛宁县| 临武县| 武鸣县| 大方县| 黄平县| 鹤壁市| 岳普湖县| 板桥市| 肥城市| 昔阳县| 平和县| 靖边县| 辉南县| 旅游| 丘北县| 盐池县| 广平县| 盐亭县| 肃宁县| 苗栗市| 肇庆市| 遵化市| 泰州市| 宁津县| 新平| 马龙县| 秦皇岛市| 揭西县| 宁河县| 义马市| 永济市| 中方县| 黔西县| 神木县| 兴山县| 临海市| 广汉市|