1. 程式人生 > >非侵入式AOP監控之——AspectJ使用

非侵入式AOP監控之——AspectJ使用

一引言
二什麼是AspectJ
2.1 它只是一個程式碼編譯器
2.2 它是用來做AOP程式設計的
2.3為什麼要用AspectJ
三AspectJ原理與運用
3.1 基本原理
3.2 使用方式
3.2.1 純註解方式
3.2.2 AspectJ語言
3.2.3 結合自定義註解使用
四 使用AspectJ進行監聽方法執行耗時
五一些比較常見的問題
六推薦文章
Demo地址

一、引言

本博文的目的不是詳細的介紹AspectJ的細節這是我來實習做的第一個任務因為老大最近讓我用非侵入式的方法監測產品方法執行時間。查了很久有各種工具但都不好用也查到了DexPosed但是它在5.0以後就不相容了因此沒用它。最後找到了AspectJ學習之後成功監測了專案方法執行耗時並找出耗時方法。因此對其作了一些使用和重要概念上的總結。
相信很多做過Web的同學對

AspectJ都不陌生Spring的AOP就是基於它而來的。如果說平常我們隨便寫寫程式的時候基本也不會用到它需要除錯的話無非就是多加一個System.out.printfln()或者Log.d()。但是由於基於面向物件的固有缺陷導致很多同模組、同一水平上的工作要在許多類中重複出現。比如說輸出日誌監控方法執行時間修改程式執行時的引數等等這樣的事情其實它們的程式碼都是可以重用的。

如果在一個大型的專案當中使用手動修改原始碼的方式來達到除錯、監控的目的第一需要插入許多重複程式碼列印日誌監控方法執行時間程式碼無法複用第二修改的成本太高處處需要手動修改分分鐘累死、眼花。

  • OOP: 面向物件把所有的事物都當做物件看待因此每一個物件都有自己的生命週期都是一個封裝的整體。每一個物件都有自己的一套垂直的系列方法和屬性使得我們使用物件的時候不需要太多的關係它的內部細節和實現過程只需要關注輸入和輸出這跟我們的思維方式非常相近極大的降低了我們的編寫程式碼成本而不像C那樣讓人頭痛。但在現實世界中並不是所有問題都能完美得劃分到模組中。舉個最簡單而又常見的例子現在想為每個模組加上日誌功能要求模組執行時候能輸出日誌。在不知道AOP的情況下一般的處理都是先設計一個日誌輸出模組這個模組提供日誌輸出API比如Android中的Log類。然後其他模組需要輸出日誌的時候呼叫Log類的幾個函式比如e(TAG,...)w(TAG,...)d(TAG,...)i(TAG,...)等。

  • AOP: 面向物件程式設計固然是開啟另一個程式設計時代但是久而久之也顯露了它的缺點最明顯的一點就是它無法橫向切割某一類方法、屬性當我們需要了解某一類方法、某一類屬性的資訊時就必須要在每一個類的方法裡面即便他們是同樣的方法只因是不同的類所以不同新增監控程式碼在程式碼量龐大的情況下這是一個不可取的方法。因此AOP編產生了基於AOP的程式設計可以讓我們橫向的切割某一類方法和屬性不需要關心他是什麼類別我覺得AOP並不是與OOP對立的而是為了彌補OOP的不足因為有了AOP我們的除錯和監控就變得簡單清晰。

二、什麼是AspectJ

2.1 它只是一個程式碼編譯器

AspectJ 意思就是Java的AspectJava的AOP。它其實不是一個新的語言它就是一個程式碼編譯器ajc後面以此代替在Java編譯器的基礎上增加了一些它自己的關鍵字識別和編譯方法。因此ajc也可以編譯Java程式碼。它在編譯期將開發者編寫的Aspect程式編織到目標程式中對目標程式作了重構目的就是建立目標程式與Aspect程式的連線耦合獲得對方的引用**獲得的是宣告型別不是執行時型別**和上下文資訊從而達到AOP的目的這裡在編譯期還是修改了原來程式的程式碼但是是ajc替我們做的。需要注意的就是AspectJ是在編譯期重構的程式碼所以獲得的物件是宣告型別如果要獲得執行時型別需要使用一些關鍵字this、target。

2.2 它是用來做AOP程式設計的

AspectJ就是AOP只不過是面向java的。AOP裡面有一些重要基本的概念

  • aspect切面實現了cross-cutting功能是針對切面的模組。最常見的是logging模組、方法執行耗時模組這樣程式按功能被分為好幾層如果按傳統的繼承的話商業模型繼承日誌模組的話需要插入修改的地方太多而通過建立一個切面就可以使用AOP來實現相同的功能了我們可以針對不同的需求做出不同的切面。

  • jointpoint連線點連線點是切面插入應用程式的地方該點能被方法呼叫而且也會被丟擲意外。連線點是應用程式提供給切面插入的地方在插入地建立AspectJ程式與源程式的連線。

    下面列表上的是被AspectJ認為是joinpoint的
    下面列表上的是被AspectJ認為是joinpoint的

  • advice處理邏輯 advice是我們切面功能的實現它是切點的真正執行的地方。比如像寫日誌到一個檔案中advice包括before、after、around等在jointpoint處插入程式碼到應用程式中。我們來看一看原AspectJ程式和反編譯過後的程式。看完下面的圖我們就大概明白了AspectJ是如何達到監控源程式的資訊了。

    原Activity程式碼
    這裡寫圖片描述

    Advise宣告和定義
    這裡寫圖片描述

    反編譯後的原始碼
    這裡寫圖片描述

  • pointcut切點 pointcut可以控制你把哪些advice應用於jointpoint上去通常你使用pointcuts通過正則表示式來把明顯的名字和模式進行匹配應用。決定了那個jointpoint會獲得通知。分為call、execution、target、this、within等關鍵字具體含義見第四節

    這裡寫圖片描述

    順便再附上一些切點匹配規則
    1匹配方法

    @註解 訪問許可權 返回值的型別 包名.函式名(引數)  
      @註解和訪問許可權public/private/protect以及static/final屬於可選項。如果不設定它們則預設都會選擇。以訪問許可權為例如果沒有設定訪問許可權作為條件那麼publicprivateprotect及staticfinal的函式都會進行搜尋。  
      返回值型別就是普通的函式的返回值型別。如果不限定型別的話就用*萬用字元表示  
      包名.函式名用於查詢匹配的函式。可以使用萬用字元包括*和..以及+號。其中*號用於匹配除.號之外的任意字元而..則表示任意子package+號表示子類。  
     比如  
     java.*.Date可以表示java.sql.Date也可以表示java.util.Date  
     Test*可以表示TestBase也可以表示TestDervied  
     java..*表示java任意子類  
     java..*Model+表示Java任意package中名字以Model結尾的子類比如TabelModelTreeModel  
     等  
      最後來看函式的引數。引數匹配比較簡單主要是引數型別比如  
     (int, char)表示引數只有兩個並且第一個引數型別是int第二個引數型別是char  
     (String, ..)表示至少有一個引數。並且第一個引數型別是String後面引數型別不限。在引數匹配中  
     ..代表任意引數個數和型別  
     (Object ...)表示不定個數的引數且型別都是Object這裡的...不是萬用字元而是Java中代表不定引數的意思  
    

    2匹配建構函式

    Constructorsignature和Method Signature類似只不過建構函式沒有返回值而且函式名必須叫new。比如  
    public *..TestDerived.new(..)  
      public選擇public訪問許可權  
      *..代表任意包名  
      TestDerived.new代表TestDerived的建構函式  
      (..)代表引數個數和型別都是任意  
    再來看Field SignatureType Signature用它們的地方見圖5。下面直接上幾個例子  
    Field Signature標準格式  
    @註解 訪問許可權 型別 類名.成員變數名  
      其中@註解和訪問許可權是可選的  
      型別成員變數型別*代表任意型別  
      類名.成員變數名成員變數名可以是*代表任意成員變數  
    比如  
    set(inttest..TestBase.base)表示設定TestBase.base變數時的JPoint  
    Type Signature直接上例子  
    staticinitialization(test..TestBase)表示TestBase類的static block  
    handler(NullPointerException)表示catchNullPointerExceptionJPoint

2.3、為什麼要用AspectJ

1、非侵入式監控 可以在不修監控目標的情況下監控其執行截獲某類方法甚至可以修改其引數和執行軌跡
2、學習成本低 它就是Java只要會Java就可以用它。
3、功能強大可拓展性高 它就是一個編譯器+一個庫可以讓開發者最大限度的發揮實現形形色色的AOP程式

三、AspectJ原理與運用

先放一塊AspectJ程式碼這裡使用的都是AspectJ較為常用的知識接著在解釋。


import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Path;
import android.os.Build;

import org.android10.gintonic.internal.ChooseDialog;
import org.android10.gintonic.internal.DebugLog;
import org.android10.gintonic.internal.MethodMsg;
import org.android10.gintonic.internal.StopWatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * 截獲類名最後含有Activity、Layout的類的所有方法
 * 監聽目標方法的執行時間
 */
@Aspect
public class TraceAspect {
  private static Object currentObject = null;
  //進行類似於正則表示式的匹配被匹配到的方法都會被截獲
  ////截獲任何包中以類名以Activity、Layout結尾並且該目標類和當前類是一個Object的物件的所有方法
  private static final String POINTCUT_METHOD =
      "(execution(* *..Activity+.*(..)) ||execution(* *..Layout+.*(..))) && target(Object) && this(Object)";
   //精確截獲MyFrameLayou的onMeasure方法
    private static final String POINTCUT_CALL = "call(* org.android10.viewgroupperformance.component.MyFrameLayout.onMeasure(..))";

  private static final String POINTCUT_METHOD_MAINACTIVITY = "execution(* *..MainActivity+.onCreate(..))";

  //切點ajc會將切點對應的Advise編織入目標程式當中
  @Pointcut(POINTCUT_METHOD)
  public void methodAnnotated() {}
  @Pointcut(POINTCUT_METHOD_MAINACTIVITY)
  public void methodAnootatedWith(){}

    /**
     * 在截獲的目標方法呼叫之前執行該Advise
     * @param joinPoint
     * @throws Throwable
     */
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  @Before("methodAnootatedWith()")
  public void onCreateBefore(JoinPoint joinPoint) throws Throwable{
      Activity activity = null;
      //獲取目標物件
      activity = ((Activity)joinPoint.getTarget());
      //插入自己的實現控制目標物件的執行
      ChooseDialog dialog = new ChooseDialog(activity);
      dialog.show();

      //做其他的操作
      buildLogMessage("test",20);
  }
    /**
     * 在截獲的目標方法呼叫返回之後無論正常還是異常執行該Advise
     * @param joinPoint
     * @throws Throwable
     */
 @After("methodAnootatedWith()")
  public void onCreateAfter(JoinPoint joinPoint) throws Throwable{
      Log.e("onCreateAfter:","onCreate is end .");

  }
    /**
     * 在截獲的目標方法體開始執行時剛進入該方法實體時呼叫
     * @param joinPoint
     * @return
     * @throws Throwable
     */
  @Around("methodAnnotated()")
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {

    if (currentObject == null){
        currentObject = joinPoint.getTarget();
    }
      //初始化計時器
    final StopWatch stopWatch = new StopWatch();
      //開始監聽
      stopWatch.start();
      //呼叫原方法的執行。
    Object result = joinPoint.proceed();
      //監聽結束
    stopWatch.stop();
      //獲取方法資訊物件
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      String className;
      //獲取當前物件通過反射獲取類別詳細資訊
      className = joinPoint.getThis().getClass().getName();

      String methodName = methodSignature.getName();
    if (currentObject != null && currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className, buildLogMessage(methodName, stopWatch.getTotalTimeMicros()),stopWatch.getTotalTimeMicros()));
    }else if(currentObject != null && !currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className, buildLogMessage(methodName, stopWatch.getTotalTimeMicros()),stopWatch.getTotalTimeMicros()));
        currentObject = joinPoint.getTarget();
        DebugLog.outPut(new Path());    //日誌儲存
        DebugLog.ReadIn(new Path());    //日誌讀取
    }
    return result;
  }

  /**
   * 建立一個日誌資訊
   *
   * @param methodName 方法名
   * @param methodDuration 執行時間
   * @return
   */
  private static String buildLogMessage(String methodName, long methodDuration) {
    StringBuilder message = new StringBuilder();
    message.append(methodName);
    message.append(" --> ");
    message.append("[");
    message.append(methodDuration);
    if (StopWatch.Accuracy == 1){
        message.append("ms");
    }else {
        message.append("mic");
    }
    message.append("]      ");

    return message.toString();
  }

}


3.1 基本原理

在編譯期對目標物件、方法做標記對目標類、方法進行重構將PointCut插入目標中截獲該目標的資訊以及上下文環境以達到非侵入程式碼監控的目的——**注意它只能獲得物件的宣告如果物件的宣告式介面那麼預設情況下不使用this、target約束切點獲取的是宣告型別而不是具體執行時的類。**

  1. 編寫Aspect宣告Aspect、PointCut和Advise。

  2. ajc編織 AspectJ編譯器在編譯期間對所切點所在的目標類進行了重構在編譯層將AspectJ程式與目標程式進行雙向關聯生成新的目標位元組碼即將AspectJ的切點和其餘輔助的資訊類段插入目標方法和目標類中同時也傳回了目標類以及其例項引用。這樣便能夠在AspectJ程式裡對目標程式進行監聽甚至操控。

  3. execution 顧名思義它截獲的是方法真正執行的程式碼區Around方法塊就是專門為它存在的。呼叫Around可以控制原方法的執行與否可以選擇執行也可以選擇替換。

//截獲任何包中以類名以Activity、Layout結尾並且該目標類和當前類是一個Object的物件的所有方法
private static final String POINTCUT_METHOD =
      "(execution(* *..Activity+.*(..)) ||execution(* *..Layout+.*(..))) && target(Object) && this(Object)";
  //基於execution的切點
  @Pointcut(POINTCUT_METHOD)
  public void methodAnnotated() {}

4 . call 同樣從名字可以看出call截獲的是方法的呼叫區它並不截獲程式碼真正的執行區域它截獲的是方法呼叫之前與呼叫之後與before、after配合使用在呼叫方法的前後插入JoinPoint和before、after通知。它截獲的資訊並沒有execution那麼多它無法控制原來方法的執行與否只是在方法呼叫前後插入切點因此它比較適合做一些輕量的監控方法呼叫耗時方法的返回值等。

 //精確截獲MyFrameLayou的onMeasure方法
    private static final String POINTCUT_CALL = "call(* org.android10.viewgroupperformance.component.MyFrameLayout.onMeasure(..))";
    //基於call的切點
    @Pointcut(POINTCUT_METHOD_MAINACTIVITY)
  public void methodAnootatedWith(){}

5 . Around替代原理目標方法體被Around方法替換原方法重新生成名為XXX_aroundBody(),如果要呼叫原方法需要在AspectJ程式的Around方法體內呼叫joinPoint.proceed()還原方法執行是這樣達到替換原方法的目的。達到這個目的需要雙方互相引用橋樑便是Aspect類目標程式插入了Aspect類所在的包獲取引用。AspectJ通過在目標類裡面加入Closure閉包類該類建構函式包含了目標類例項、目標方法引數、JoinPoint物件等資訊同時該類作為切點原方法的執行代理該閉包通過Aspect類呼叫Around方法傳入Aspect程式。這樣便達到了關聯的目的便可以在Aspect程式中監控和修改目標程式。

/**
     * 在截獲的目標方法體開始執行時剛進入該方法實體時呼叫
     * @param joinPoint
     * @return
     * @throws Throwable
     */
  @Around("methodAnnotated()")
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {

    if (currentObject == null){
        currentObject = joinPoint.getTarget();
    }
      //初始化計時器
    final StopWatch stopWatch = new StopWatch();
      //開始監聽
      stopWatch.start();
      //呼叫原方法的執行。
    Object result = joinPoint.proceed();
      //監聽結束
    stopWatch.stop();
      //獲取方法資訊物件
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      String className;
      //獲取當前物件通過反射獲取類別詳細資訊
      className = joinPoint.getThis().getClass().getName();

      String methodName = methodSignature.getName();
    if (currentObject != null && currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className, buildLogMessage(methodName, stopWatch.getTotalTimeMicros()),stopWatch.getTotalTimeMicros()));
    }else if(currentObject != null && !currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className, buildLogMessage(methodName, stopWatch.getTotalTimeMicros()),stopWatch.getTotalTimeMicros()));
        currentObject = joinPoint.getTarget();
        DebugLog.outPut(new Path());    //日誌儲存
        DebugLog.ReadIn(new Path());    //日誌讀取
    }
    return result;
  }

6 . Before與After Before與After只是在方法被呼叫前和呼叫之後新增JoinPoint和通知方法直接插入原程式方法體中,呼叫AspectJ程式定義的Advise方法它並不替代原方法是在方法call之前和之後做一個插入操作。After分為returnning和throwing兩類前者是在正常returning之後呼叫後者是在throwing發生之後呼叫。預設的After是在finally處呼叫因此它包含了前面的兩種情況。

   /**
     * 在截獲的目標方法呼叫之前執行該Advise
     * @param joinPoint
     * @throws Throwable
     */
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  @Before("methodAnootatedWith()")
  public void onCreateBefore(JoinPoint joinPoint) throws Throwable{
      Activity activity = null;
      //獲取目標物件
      activity = ((Activity)joinPoint.getTarget());
      //插入自己的實現控制目標物件的執行
      ChooseDialog dialog = new ChooseDialog(activity);
      dialog.show();

      //做其他的操作
      buildLogMessage("test",20);
  }
    /**
     * 在截獲的目標方法呼叫返回之後無論正常還是異常執行該Advise
     * @param joinPoint
     * @throws Throwable
     */
 @After("methodAnootatedWith()")
  public void onCreateAfter(JoinPoint joinPoint) throws Throwable{
      Log.e("onCreateAfter:","onCreate is end .");

  }

7 . 重要關鍵字 
在其它關鍵字中必須要注意的就是this、target的使用和區別同時還有一個很重要的方法**Signature.getDeclaringType();**AspectJ是在編譯期截獲的物件資訊因此它獲得的標籤只是物件的宣告比如介面、抽象類而不是執行時具體的物件。如果想要獲得執行時物件就需要用this、target關鍵字

this  用於匹配當前AOP代理物件型別的執行方法注意是AOP代理物件的型別匹配這樣就可能包括引入介面也型別匹配
target用於匹配當前目標物件型別的執行方法注意是目標物件的型別匹配這樣就不包括引入介面也型別匹配
args用於匹配當前執行的方法傳入的引數為指定型別的執行方法
within用於匹配指定型別內的方法執行

更加詳細的解說請參考深入理解Android之AOP該博文對於AspectJ的其他詳細概念、定義、細節示例解說的非常清楚如果想要詳細瞭解請務必要看。

3.2 使用方式

3.2.1 純註解方式

上面貼的程式碼就是該方式也是最普遍的方式它不需要其他外掛的支援Eclipse中有AJDT可以支援AspectJ關鍵字宣告但Android Studio中沒有改外掛使用Java的註解和ajc以及它的庫就可以完成AOP程式設計非常方便而且可以在絕大部分支援Java的IDE中使用。缺點就是對於註釋部分的匹配沒有檢錯功能。


/**
 * Created by lingyi.mly on 2016/5/21.
 */
@Aspect
public class TraceAspect3 {
    private static volatile Object currentObject = null;
    private ExecutorService ThreadPool = Executors.newFixedThreadPool(10);
    private static final String POINTCUT_METHOD =
            "call(* *.*(..))&&target(Object) &&!within(*.TimeMonitorFragment)";
    @Pointcut(POINTCUT_METHOD)
    public void methodAnnotated() {
    }

    StopWatch stopWatch;
    MethodSignature methodSignature;
    String methodName;
    String className;

    @Before("methodAnnotated()")
    public void beforeInvoked(final JoinPoint joinPoint) {
        className = "call target: " + joinPoint.getTarget().getClass().getName();
        methodSignature = (MethodSignature) joinPoint.getSignature();
        methodName = methodSignature.getName();
        stopWatch = new StopWatch();
        stopWatch.start();
    }

    @After("methodAnnotated()")
    public void afterInvoked(final JoinPoint joinPoint) {
        stopWatch.stop();
        double methodDuration = stopWatch.getTotalTime(StopWatch.Accuracy);
        DebugLog.log(new MethodMsg(className, methodName, methodDuration, StopWatch.Accuracy));
    }
}

3.2.2 AspectJ語言

在Eclipse中使用AJDT外掛,可以識別AspectJ的語法。這樣編寫起來相對於註解要方便許多還提供檢錯功能比較強大。不過不是所有的IDE都支援比如Android Studio目前就沒有我哭了好久。

package main;
import java.util.HashMap;
import java.util.Map;
/**
 * 只有call才能區分this 與target 在與的情況下兩者不共存在交的情況下共存。
 * execution匹配this 與 target時無論是 與 還是 交集 都是同一個物件
 * @author lingyi.mly
 *
 */


public aspect Aspect{
    static int count = 0;
    pointcut targetTest() : call(* main.*.*(..)) &&( target(Object) );
    pointcut thisTest( ) : execution(* main.*.*(..)) && (target(Object) ||this(Object));
    Object around() : thisTest() {
        if (thisJoinPoint.getThis() != null) {
            System.out.println(thisJoinPoint.getThis().getClass().getName()  +  "   " + thisJoinPoint.getSourceLocation());
        }else if (thisJoinPoint.getTarget() != null) {
            System.out.println(thisJoinPoint.getTarget().getClass().getName()  +  "   " + thisJoinPoint.getSourceLocation());
        } 
        return null;
    }
    before() : targetTest() {
        if (thisJoinPoint.getThis() != null) {
            System.out.println("this:  "+thisJoinPoint.getThis().getClass().getName()  +  "   " + thisJoinPoint.getSourceLocation());
        }else if (thisJoinPoint.getTarget() != null) {
            System.out.println("target:  "+thisJoinPoint.getTarget().getClass().getName()  +  "   " + thisJoinPoint.getSourceLocation());
        } 
    }

    private static Map<String, Integer> threadMap = new HashMap<String,Integer>();
}

3.2.3 結合自定義註解使用

這個是混合用法可以在execution、call中使用註解然後該註解標註在目標方法上就可以實現關聯並且截獲。這樣做的好處實在想不到最多就是可以精確定位到某一個方法那使用絕對路徑匹配不也可以。而且還侵入了原始碼。實在是不推薦不過我在網上看到有人這麼用了所以也貼上來了。如果哪位高手知道這樣做的精髓請一定指教。下面貼一下它的用法實現。

自定義註解及被標記的方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 表明被註釋的方法將被跟蹤僅在Debug模式下並且將會與Aspect程式中截獲該註釋的Advise關聯呼叫該切點
 * 的Advise
 */
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
public @interface DebugTrace {}



/**
 * 被註解的類
 */
public class MyFrameLayout extends FrameLayout {
  //........  
  //被註解的方法
  @DebugTrace
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }
  @DebugTrace
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
  }
}

切面

package org.android10.gintonic.aspect;

/**
 * 跟蹤被DebugTrace註解標記的方法和建構函式
 */
@Aspect
public class TraceAspect {
  //跟蹤DebugTrace註解
  private static final String POINTCUT_METHOD =
      "execution(@org.android10.gintonic.annotation.DebugTrace * *(..))";

  @Pointcut(POINTCUT_METHOD)
  public void methodAnnotatedWithDebugTrace() {}

  @Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
    // Do SomeThing
    stopWatch.start();
    Object result = joinPoint.proceed();
    stopWatch.stop();
    // Do SomeThing
    return result;
  }
  // ........省略
}

四、DEMO——監聽方法執行耗時列印並輸出

源程式程式碼

關鍵程式碼

private static final String POINTCUT_METHOD =
      "(execution(* *..Activity+.*(..)) ||execution(* *..Layout+.*(..))) && target(Object) && this(Object)";
  // ...........
  @Pointcut(POINTCUT_METHOD)
  public void methodAnnotated() {}
  // .........

  @Around("methodAnnotated()")
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {

    if (currentObject == null){
        currentObject = joinPoint.getTarget();
    }
      //初始化計時器
    final StopWatch stopWatch = new StopWatch();
      //開始監聽
      stopWatch.start();
      //呼叫原方法的執行。
    Object result = joinPoint.proceed();
      //監聽結束
    stopWatch.stop();
      //獲取方法資訊物件
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      String className;
      //獲取當前物件通過反射獲取類別詳細資訊
      className = joinPoint.getThis().getClass().getName();

      String methodName = methodSignature.getName();
      
            
           

相關推薦

侵入AOP監控——AspectJ使用

一引言 二什麼是AspectJ 2.1 它只是一個程式碼編譯器 2.2 它是用來做AOP程式設計的 2.3為什麼要用AspectJ 三AspectJ原理與運用 3.1 基本原理 3.2 使用方式 3.2.1 純註解方式 3.2.2 AspectJ語言 3.2.3 結合自定義註解使用 四 使用Aspect

利用AspectJ實現Android端侵入埋點

前言 最近在專案中遇到通過埋點對使用者行為進行收集的需求,由於專案執行在區域網,而且有一些很細化的需求,比較幾種技術方案之後,選擇了通過AspectJ進行埋點。本文主要介紹筆者對學習和使用AspectJ的總結。 AspectJ是什麼 正如面向物件程式設

系統性能監控系列1:使用JAVA動態代理實現侵入的效能測量方法

歡迎關注公眾號: 當我們開發的服務上線後,線上的系統執行狀態(是否正常,效能是否滿足需求)等等就成了架構師和研發工程師關心的問題 。對於系統監控有很多維度,比如:監控CPU,磁碟IO,監控服務請求的響應時間等。相對於這些來說,我今天要給大家分享的是具體的程式碼層次的

使用phpAnalysis打造PHP應用侵入效能分析器

使用phpAnalysis打造PHP應用非侵入式效能分析器,查詢PHP效能瓶頸。 什麼是phpAnalysis phpAnalysis是一款輕量級非侵入式PHP應用效能分析器,適用於開發、測試及生產環境部署使用,方便開發及測試工程師診斷效能問題: 通過tideways收集PHP程式單步執行過程中所

android AOP實現AspectJ

AOP 1.1 背景 OOP(面向物件程式設計)的精髓是把功能或問題模組化,每個模組都有自己的職責,理想狀態是隻處理自己職責之內的事務。但在實際中,理想的職責單一往往攜帶了一些其他的、“髒”的邏輯處理。舉個最簡單而又常見的例子:現在想為模組A加上日誌功能

侵入侵入的區別

簡單說一下我的理解吧。假設大家都想要把使用者程式碼塞到一個框架裡。侵入式的做法就是要求使用者程式碼“知道”框架的程式碼,表現為使用者程式碼需要繼承框架提供的類。非侵入式則不需要使用者程式碼引入框架程式碼的資訊,從類的編寫者角度來看,察覺不到框架的存在。 例如:  使用struts的時候,我需要繼承一些stru

Android中的AOP程式設計AspectJ實戰實現資料埋點

文章背景 最近在給某某銀行做專案的時,涉及到了資料埋點,效能監控等問題,那我們起先想到的有兩種方案,方案之一就是藉助第三方,比如友盟、Bugly等,由於專案是部署在銀行的網路框架之內的,所以該方案不可行。另外一種方案是就是給每一個方法裡面資料打點,然後寫入S

Win32下的侵入協程實現

關於協程和libco 本專案Github地址   協程實現非同步其實就是用同步的業務邏輯程式碼,但內部卻執行非同步等待並進行排程,既保證的程式碼的可讀性,又能實現非同步的高併發。在Windows程式設計中,往往會有大量阻塞的IO操作,比如這段程式碼:

go語言侵入介面

侵入式介面:需要顯式地建立一個類去實現一個介面。 非侵入式介面:不需要顯式地建立一個類去實現一個介面。 C++侵入式介面: #include <iostream> enum SEX

【轉】侵入設計 和侵入設計 意思?

非侵入式系介紹DI用語,我得理解是兩個元件(類,介面)之間,比較獨立,不深入到另一個類內部,哪位大蝦能點撥一二? 關於“侵入式”和“非侵入式”  有讀者講“侵入式”這一術語無法理解,這裡給一個簡單解釋,是我個人的看法。  在設計一個類時,按理說,需要的應該只是該類所企圖表示的那個“概念”本身:為表示有

侵入侵入框架的區別

什麼是框架的侵入性? 一句話概括就是,你的程式碼需要依賴框架的程式碼,如果把框架拿掉或者換一個框架,就需要重新修改程式碼。 舉個很簡單的栗子啦: 如果使用侵入式的框架,一般需要繼承或者實現框架的某一個類或介面,這樣你把框架拿到以後就會執行不起來,甚至在程式碼層

接觸通訊技術RFID

什麼是RFID技術?   RFID射頻識別是一種非接觸式的自動識別技術,它通過射頻訊號自動識別目標物件並獲取相關資料,識別工作無須人工干預,可工作於各種惡劣環境。RFID技術可識別高速運動物體並可同時識別多個標籤,操作快捷方便。        RFID是一種簡單的無線系統

【Spring】—AOPAspectJ註解方式實現聲明事務管理

source xml配置 blog org 僅支持 選擇 imp 獨立 col 前言 這回來說下註解方式的聲明式事務管理。 正文 Demo 1、引入相關的jar包這裏寫圖片描述 2、引入AOP約束<beans xmlns:xsi="http://www.w3

springAspectJ實現AOP

添加 result exe 包名 inter string ring service odin 一、基於註解的AspectJ 1、要在spring應用中使用AspectJ註解,必須在classpath下包含AspectJ類庫: com.springsource.org

使用Redisson實現分布鎖,Spring AOP簡化

獲取鎖 rip dss 執行c lease dset wire template alt 源碼 Redisson概述 Redisson是一個在Redis的基礎上實現的Java駐內存數據網格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對

Zabbix監控分布監控zabbix_proxy

zabbix zabbix_proxy 分布式監控 Zabbix分布式監控zabbix proxy 可以代替 zabbix server 檢索客戶端的數據,然後把數據匯報給 zabbix server,並且在一定程度上分擔了zabbix server 的壓力.zabbix proxy 可以非常簡便

springAspectJ AOP編程

.html 支持 實現 spec .com 使用 lis 類型 href 一、引言:   AspectJ框架不僅實現了面向切面編程,而且還支持註解,spring將它引入自己的規範之中。 二、需要了解: AspectJ是基於java語言的AOP框架 spring2.0之

zabbix分布監控zabbix-proxy實戰

zabbix主動監控 zabbix模板克隆 zabbix分布式配置 zabbix-proxy配置 一、概述: zabbix 是一個分布式的監控系統,支持通過代理服務器zabbix proxy收集zabbix agent的數據,然後把收集保存在本地數據庫並發送給zabbix server進行統

Linux expect 交換命令

expect 非交換式 命令 expect 非交換式命令 測試實例:非交換式登陸某一臺服務器,通常第一次登陸一臺服務器的時候,需要按一下yes,然後再輸入密碼,我們目的是通過expect模擬輸入 [root@localhost ~]# ssh 10.3.151.31 The authenticit

spring-AOP(二)實現原理AspectJ註解方式

在上一篇spring-AOP(一)實現原理我們瞭解瞭如何使用ProxyFactory來建立AOP代理物件,但其過程需要實現一些介面,並且需要一些比較複雜的配置。因此,在spring2.0之後,提供了一種較為便利的方式。 使用@Aspect註解宣告一個切面類,之後通過@EnableAspectJAutoProx