AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法,它有一個專門的編譯器用來生成遵照Java字節編碼規範的Class文件.利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。java
數據埋點,日誌記錄,性能統計,安全控制,事務處理,異常處理等等android
將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。git
AspectJ在代碼的編譯期間掃描目標程序,根據切點(PointCut)匹配,將開發者編寫的Aspect程序編織(Weave)到目標程序的.class文件中,對目標程序做了重構(重構單位是JoinPoint),目的就是創建目標程序與Aspect程序的鏈接(得到執行的對象、方法、參數等上下文信息),從而達到AOP的目的。github
要引入AspectJ到Android工程中,最重要的就是兩個包:正則表達式
//在buildscript中添加該編織器,gradle構建時就會對class文件進行編織 //在buildscript中添加該工具包,在構建工程的時候執行一些任務:打日誌等安全
classpath 'org.aspectj:aspectjtools:1.8.11'
classpath 'org.aspectj:aspectjweaver:1.8.9'
複製代碼
//在dependencies中添加該依賴,提供@AspectJ語法閉包
compile 'org.aspectj:aspectjrt:1.8.9'
複製代碼
//在app中的gradleapp
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
複製代碼
//打印gradle日誌框架
android.applicationVariants.all { variant ->
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(
File.pathSeparator)]
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler)
def log = project.logger for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
複製代碼
fernandocejas.com/2014/08/03/…函數
在AOP中切面是標記執行被切點標記方法的邏輯處理。就是切點要處理的具體邏輯在切面這個模塊。
它是切面插入執行應用程序的地方,他能被方法調用,也能新增方法,JoinPoint是一個執行鏈,每個JoinPoint是一個單獨的閉包,在執行的時候將上下文環境賦予閉包執行方法體邏輯。
AspectJ的joinpoint有以下:
method call 函數調用
method execution 函數執行
constructor call 構造函數調用
constructor execution 構造函數執行
field get 獲取某個變量
field set 設置某個變量
pre-initialization Object在構造函數中作得一些工做。
initialization Object在構造函數中作得工做
static initialization 類初始化
handler 異常處理,好比try catch(xxx)中,對應catch內的執行
advice execution 裏面有3個標記(Around Before After)
複製代碼
切點的聲明決定須要切割的JoinPoint的集合,Pointcut能夠控制你把哪些advice應用於JoinPoint上去,一般經過正則表達式來進行匹配應用,決定了那個jointpoint會得到通知。分爲call、execution、target、this、within等關鍵字等。
within(TypePattem) TypePattern標示package或者類。TypePatter可使用通配符
withincode(ConstructorSignaturelMethodSignature) 表示某個構造函數或其餘函數執行過程當中涉及到的JPoint
cflow(pointcuts) cflow是call flow的意思,cflow的條件是一個pointcut
cflowbelow(pointcuts) 表示調用pointcuts函數時所包含的JPoint,不包括pointcuts這個JPoint自己
this(Type) JPoint的this對象是Type類型
target(Type) JPoint的target對象是Type類型
args(TypeSignature) 用來對JPoint的參數進行條件搜索的
複製代碼
(1)類型匹配語法 類型匹配的通配符: *:匹配任何數量字符; ..:匹配任何數量字符的重複,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數。 +:匹配指定類型的子類型;僅能做爲後綴放在類型模式後邊。 AspectJ使用 且(&&)、或(||)、非(!)來組合切入點表達式。
(2)匹配模式 call(<註解?> <修飾符?> <返回值類型> <類型聲明?>.<方法名>(參數列表) <異常列表>?)
精確匹配
//表示匹配 com.sunmi.MainActivity類中全部被@Describe註解的public void方法。
@Pointcut("call(@Describe public void com.sunmi.MainActivity.init(Context))")
public void pointCut(){}
單一模糊匹配
//表示匹配 com.sunmi.MainActivity類中全部被@Describe註解的public void方法。
@Pointcut("call(@Describe public void com.sunmi.MainActivity.*(..)) ")
public void pointCut(){}
//表示匹配調用Toast及其子類調用的show方法,不論返回類型以及參數列表,而且該子類在以com.sunmi開頭的包名內
@Pointcut("call(* android.widget.Toast+.show(..)) && (within(com.sunmi..*))")
public void toastShow() {
}
組合模糊匹配
//表示匹配任意Activity或者其子類的onStart方法執行,不論返回類型以及參數列表,且該類在com.sunmi包名內
@Pointcut("execution(* *..Activity+.onStart(..))&& within(com.sunmi.*)")
public void onStart(){}
複製代碼
聲明一個註解 AopPoint
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AopPoint {
String value();
int type() default 0;
}
複製代碼
在Activity 中,定義了2個按鈕的點擊事件,在方法上都標記註解,指定了value 和type。
@AopPoint(value = "首頁點擊",type = 1)
public void doFunc1(View view) {
SystemClock.sleep(1000);
}
@AopPoint("分類點擊")
public void doFunc2(View view) {
SystemClock.sleep(1000);
}
複製代碼
編寫一個切面類
@Aspect
public class AopAspect {
@Pointcut("execution(@com.example.davis.aspectdemo.AopPoint * *(..))")
public void aopCut(){
}
@Around("aopCut()")
public Object dealMethod(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature= (MethodSignature) joinPoint.getSignature();
AopPoint aopPoint=signature.getMethod().getAnnotation(AopPoint.class);
String value=aopPoint.value();
int type=aopPoint.type();
TimeTool timeTool=new TimeTool();
timeTool.start();
Object result=joinPoint.proceed();
timeTool.stop();
Log.e("aopCut",value+" 執行時間="+timeTool.getTotalTimeMillis() +" type類型是"+type);
return result;
}
}
複製代碼
結果
一、這個是掃描Activity 中onCreate方法的調用
@Aspect
public class FuncTAspect {
@Before("execution(* android.app.Activity.onCreate(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString();
Log.e("FuncTAspect", "onActivityMethodBefore: " + key+"\n"+joinPoint.getThis());
}
}
複製代碼
結果只能掃描到onCreate方法
若是把onCreate改爲通配符* android.app.Activity.onCreate(..) 改爲android.app.Activity.*(..)
結果
二、捕獲catch異常
@Aspect
public class ExceptionHandleAspect {
private static final String TAG = "ExceptionHandleAspect";
/** * 截獲空指針異常 * * @param e */
@Pointcut("handler(java.lang.Exception)&&args(e)")
public void handle(Exception e) {
}
/** * 在catch代碼執行以前作一些處理 * * @param joinPoint * @param e 異常參數 */
@Before(value = "handle(e)", argNames = "e")
public void handleBefore(JoinPoint joinPoint, Exception e) {
Log.e(TAG, joinPoint.getSignature().toLongString() + " handleBefore() :" + e.toString());
}
}
複製代碼
在方法裏製造一個Null指針
public void doFunc1(View view) {
try {
AppItem appItem=null;
appItem.number=2;
}catch (Exception e){}
}
複製代碼
結果