[AOP]AspectJX基本使用

AspectJX基於AspectJ並在此基礎上擴展出來可應用於Android開發平臺的AOP框架,可作用於java源碼,class文件及jar包,同時支持kotlin的應用。

引入

  • 插件引用

在項目根目錄的build.gradle裏依賴AspectJX

dependencies {
    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
}

或者使用product目錄下的jar包,在你的項目根目錄下新建目錄plugins,把product/gradle-android-plugin-aspectjx-2.0.0.jar拷貝到plugins,依賴jar包

dependencies {
    classpath fileTree(dir:'plugins', include:['*.jar'])
}
  • 在app項目的build.gradle裏應用插件
apply plugin: 'android-aspectjx'
//或者這樣也可以
apply plugin: 'com.hujiang.android-aspectjx'

AOP可以做什麼

  • 日誌
  • 持久化
  • 性能監控
  • 數據校驗
  • 緩存

AspectJ核心語法簡介

AspectJ 其實就是一種 AOP 框架,AOP 是實現程序功能統一維護的一種技術。利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合性降低,提高程序的可重用性,同時大大提高了開發效率。因此 AOP 的優勢可總結爲如下 兩點:

  • 無侵入性。
  • 修改方便。

下面介紹一下AspectJ的一些核心概念

  • 橫切關注點:對哪些方法進行攔截,攔截後怎麼處理
  • 切面(Aspect):對橫切關注點的抽象
  • 連接點(JoinPoint):是程序的關鍵執行點,也是我們關注的點,它是指被攔截到的點(方法、字段、構造器等)
  • 切入點(PointCut):對JoinPoint進行攔截的定義。PointCut的目的是提供一種方法使得開發者能夠選擇自己感興趣的JoinPoint
  • 通知(Advice):切入點僅用於捕捉連接點集合,但是,除了捕捉連接點集合以外什麼事情都沒有做。事實上實現橫切行爲我們要使用通知。它 一般指攔截到 JoinPoint 後要執行的代碼,分爲 前置、後置、環繞 三種類型。這裏,我們需要注意 Advice Precedence(優先權) 的情況,比如我們對同一個切面方法同時使用了 @Before@Around 時就會報錯,此時會提示需要設置 Advice 的優先級。

AspectJ 作爲一種基於 Java 語言實現的一套面向切面程序設計規範。它向 Java 中加入了 連接點(Join Point) 這個新概念,其實它也只是現存的一個 Java 概念的名稱而已。它向 Java 語言中加入了少許新結構,譬如切入點(pointcut)、通知(Advice)、類型間聲明(Inter-type declaration) 和切面(Aspect)。切入點和通知動態地影響程序流程,類型間聲明則是靜態的影響程序的類等級結構,而切面則是對所有這些新結構的封裝。

對於AsepctJ 中的各個核心概念來說,其連接點就恰如程序流中適當的一點。而切入點收集特定的連接點集合和在這些點中的值。一個通知則是當一個連接點到達時執行的代碼,這些都是 AspectJ 的動態部分。其實連接點就好比是程序中那一條一條的語句,而切入點就是特定一條語句處設置的一個斷點,它收集了斷點處程序棧的信息,而通知就是在這個斷點前後想要加入的程序代碼。

此外,AspectJ中也有許多不同種類的類型間聲明,這就允許程序員修改程序的靜態結構、名稱、類的成員以及類之間的關係。 AspectJ中的切面是橫切關注點的模塊單元。它們的行爲與 Java語言中的類很象,但是切面 還封裝了切入點、通知以及類型間聲明。

AspectJ的語法

  • JoinPoint
    一般定位在如下位置:
    (1)函數調用
    (2)獲取、設置變量
    (3)類初始化
    使用 PointCut 對我們指定的連接點進行攔截,通過Advice,就可以攔截到 JoinPoint 後要執行的代碼。Advice 通常有以下 三種類型:
    (1)BeforePointCut 之前執行。
    (2)AfterPointCut 之後執行。
    (3)AroundPointCut 之前、之後分別執行。

示例1,最簡單使用:

@Before("execution(* android.app.Activity.on**(..))")    
public void onActivityCalled(JoinPoint joinPoint) throws Throwable {     
   Log.d(...)    
}

其中,在 execution 中的是一個匹配規則,第一個 * 代表匹配任意的方法返回值,後面的語法代碼匹配所有 Activity 中以 on 開頭的方法。這樣,我們就可以 在 App 中所有 Activity 中以 on 開頭的方法中輸出一句log。

上面的 execution 就是處理 Join Point 的類型,通常有如下兩種類型:
(1)call:代表調用方法的位置,插入在函數體外面。
(2)execution:代表方法執行的位置,插入在函數體內部。

示例2,統計 Application 中的所有方法耗時:

@Aspect    
public class ApplicationAop {       
     @Around("call (* com.json.chao.application.BaseApplication.**(..))")        
     public void getTime(ProceedingJoinPoint joinPoint) {    
         Signature signature = joinPoint.getSignature();        
         String name = signature.toShortString();        
         long time = System.currentTimeMillis();        
         try {       
              joinPoint.proceed();        
         } catch (Throwable throwable) {       
              throwable.printStackTrace();        
         }        
         Log.i(TAG, name + " cost" +     (System.currentTimeMillis() - time));        
     }    
}

需要注意的是,當Advice爲Before或After時,函數參數爲JoinPoint,當爲Around時,函數參數爲ProceedingJoinPoint。而Around和Before及After最大的區別是,ProceedingJoinPoint不同於JoinPoint,其內部提供了proceed方法來執行目標函數。

參考文章

深入探索編譯插樁技術(二、AspectJ)
AOP 之 AspectJ 全面剖析 in Android

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章