欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

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

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

成都創(chuàng)新互聯(lián)是一家專(zhuān)注于網(wǎng)站制作、做網(wǎng)站與策劃設(shè)計(jì),梅州網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)十余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:梅州等地區(qū)。梅州做網(wǎng)站價(jià)格咨詢(xún):13518219792

背景

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

問(wèn)題重現(xiàn)

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

定義一個(gè)注解:

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.");
    }
  }

}

方法測(cè)試:

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é)果:

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

debug分析

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

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);
  }
}

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

method-call和method-execution都是連接點(diǎn)ProceedingJoinPoint的kind屬性

其實(shí),這屬于Ajc編譯器的一個(gè)Bug,詳見(jiàn)Ajc-bug

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

解決方法

方案一

因?yàn)閮纱握{(diào)用的kind屬性不一樣,所以可以通過(guò)kind屬性來(lái)判斷時(shí)候調(diào)用切面。這樣顯得不優(yōu)雅,而且如果切面有更多的邏輯的話(huà),需要加各種if-else的判斷,所以不推薦。

方法二

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

重新運(yùn)行上面的測(cè)試類(lèi),結(jié)果如下:

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

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

當(dāng)前名稱(chēng):分析java中AspectJ切面執(zhí)行兩次的原因
分享鏈接:http://www.chinadenli.net/article42/ihesec.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航微信小程序定制開(kāi)發(fā)網(wǎng)站排名Google營(yíng)銷(xiāo)型網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管