AspectJX學習筆記
參考文檔:
Introduction to AspectJ
Github: AspectjDemo
Android監測用戶行為之中AOP編程之AspectJ實戰 作者:weixin_33726943
Android中的AOP的實現及AspectJ的使用 作者:岩漿李的游魚
深入理解Android之AOP 作者:阿拉神農
常用概念
Join Points(連接點):簡稱JPoints,是AspectJ的核心思想之一,把程序的整個執行過程划分成幾個關鍵點,包括構造方法調用,調用方法,方法執行,拋出異常等。這些關鍵點可以作為Join Points,將想要執行的內容插入到這些時機中。
Pointcuts(切入點):指定相應Advice需要插入的位置。
Advice(通知):指定代碼將注入到Pointcuts的什么位置。
Aspect(切面):Pointcut和Advice組合到一起,形成了一個明確的位置,也就是切面。
一、Pointcuts切入點
用來描述 JPoint 注入點的一段表達式,比如:調用 Animal 類 fly 方法的地方,call(* Animal.fly(..))。
時機 | 命令 |
---|---|
函數執行(在函數內部) | execution(MethodPattern) |
函數調用(調用函數的位置) | call(MethodPattern) |
構造函數執行 | execution(ConstructorPattern) |
構造函數調用 | call(ConstructorPattern) |
靜態初始化 | staticinitialization(TypePattern) |
讀取屬性 | get(FieldPattern) |
設定屬性 | set(FieldPattern) |
異常處理(對應catch內的執行) | handler(TypePattern) |
Advice執行時 | adviceexecution() |
1、明確指定切入點
方法簽名為void Point.setX(int),Point的setX方法,入參是1個int類型,返回值為void
call(void Point.setX(int))
2、使用條件運算符指定切入點
可以通過&&,||,!進行邏輯運算。
call(void Point.setX(int)) ||
call(void Point.setY(int))
3、使用通配符指定切入點
篩選出Figure類,函數名以make開頭,任意參數,返回值為void的方法。
call(void Figure.make*(..))
4、限定訪問權限的切入點
限定Figure類,任意函數名,任意參數,任意返回值,public方法。
call(public * Figure.* (..))
二、Advice建議
常見的有 Before、After、Around 等,表示代碼執行前、執行后、替換目標代碼,也就是在 Pointcut 何處注入代碼。
注解 | 用途 |
---|---|
Before | 在執行JPoint之前 |
After | 在執行JPoint之后 |
Around | 替換原需要執行的代碼,如果需要執行源代碼,可以使用jointPoint.proceed()方法。 |
AfterReturning | 正常return時 |
AfterThrowing | 拋出異常時 |
三、AspectJX使用
AspectJX是滬江網開源的一個支持Android的AspectJ庫。Github:gradle_plugin_android_aspectjx
1、環境配置
- 在根目錄的build.gradle增加AspectJX的依賴。
dependencies {
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
}
- 修改需要使用AspectJX的模塊的build.gradle。
dependencies {
// ..
implementation 'org.aspectj:aspectjrt:1.9.5'
// ..
}
apply plugin: 'android-aspectjx'
aspectjx {
//排除部分路徑
exclude 'android'
exclude 'com.alibaba'
}
2、創建Aspect類
需要在類上注解@Aspect,函數上注解需要注入的路徑
returning參數可以指明返回值。
package com.irisleon.fridge.util;
import android.util.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class TrackHelper {
private static final String TAG = "TrackHelper";
@Before("execution(* com.irisleon.fridge.*.*.*(..)) || execution(* com.irisleon.fridge.*.*(..))")
public void DebugFunctionLog(JoinPoint joinPoint) throws Throwable {
Log.d(TAG, "----> FuncTrace:" + joinPoint.getSignature());
Log.d(TAG, " At:" + joinPoint.getSourceLocation());
for (Object item : joinPoint.getArgs()) {
if (item != null) {
Log.d(TAG, " Arg:" + item.toString());
}
}
}
@AfterReturning(pointcut = "execution(* com.irisleon.fridge.*.*.*(..)) || execution(* com.irisleon.fridge.*.*(..))",
returning = "retVal")
public void DebugReturnLog(JoinPoint joinPoint, Object retVal) throws Throwable {
Log.d(TAG, "<---- FuncTrace:" + joinPoint.getSignature());
if (retVal != null) {
Log.d(TAG, " Return:" + retVal);
}
}
}
3、效果展示
4、使用場景
1)收集用戶點擊事件,入參。不需要侵入式修改每一個點擊事件,只需要統一監聽onClick,onTouch等事件。
2)判斷某個函數是否有調用權限。例如@Around注解。判斷用戶是否已經登錄。如果未登錄,或者沒有權限,則不繼續進行處理。如果可以繼續操作,調用jointPoint.proceed()。