使用AspectJ在Android中實現Aop
開題
上一篇文章Android Aop預研中介紹了Aop的各種實現方式,並且在最後提到,選擇AspectJ作為合適的開發方式。這篇文章通過我自己編寫的一個例子,來說明AspectJ的使用。
可以這樣說,上面的文章將我所要介紹的大部分關於AOP的內容都包含了,而且講解得很明晰易懂,建議在閱讀本文之前,先讀上面的文章。
另外,本文不對AspectJ相關的基本語法做介紹,在閱讀本文前,最好對AspectJ有基本的瞭解,由於網上的朋友,對這些內容有大量的介紹,我也不去做這些重複的工作。推薦文章為深入理解Android之AOP。
最後,還需要提醒大家,本文專案程式碼中,使用到自定義gradle外掛的相關知識,這裡也不贅述,推薦文章為
本文的用意
看到此處的朋友可以會問,既然上面的幾篇文章,將你要講的東西都說完了,我們直接去看不就成了。答案是,的確是這樣的。那麼我寫這篇文章的用意是什麼呢?
首先主要是一個總結的目的,再者本文參考的主要專案程式碼為JakeWharton大神的Hugo。Hugo是一個非常容易使用、易擴充套件的Aop例子,上面的【翻譯】Android中的AOP程式設計
。也提到這個專案,是比文章介紹的專案更加完整的。
但是Hugo使用到的不止Aspect的知識,並且自定義了一個gradle外掛方便專案裡有ajc編譯器進行編譯(因為一個app必然會依賴其他lib,我們可以在lib和app中都使用aop,但是都要使用ajc對其編譯,gradle外掛的好處就是將編譯設定抽出,成為一個app,lib都可以使用的模組)。如果沒有上面三篇文章的基礎,是難以理解Hugo專案的。
因此,我對Hugo專案進行了一些探究,將其aspectj和gradle模組分離,模仿其方式實現了一個gradle外掛,並且實現了Aop。
本文只對專案程式碼做簡單介紹,需要具體瞭解的朋友,在閱讀完三篇文章以後,可以下載專案程式碼直接執行,檢視效果。
使用AspectJ
使用AspectJ的方法很簡單,首先是定義一個DebugLog註解
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.CONSTRUCTOR ;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
@Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(CLASS)
public @interface DebugLog {
}
接下來就是使用AspectJ的方式,其設定PointCut(什麼?你不知道PointCut?說明你沒有看上面推薦的文章。。。)
@Aspect
public class Hugo {
//帶有DebugLog註解的所有類
@Pointcut("within(@com.example.aoplib.DebugLog *)")
public void withinAnnotatedClass() {}
//在帶有DebugLog註解的所有類,除去synthetic修飾的方法
@Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()")
public void methodInsideAnnotatedType() {}
//在帶有DebugLog註解的所有類,除去synthetic修飾的構造方法
@Pointcut("execution(!synthetic *.new(..)) && withinAnnotatedClass()")
public void constructorInsideAnnotatedType() {}
//在帶有DebugLog註解的方法
@Pointcut("execution(@com.example.aoplib.DebugLog * *(..)) || methodInsideAnnotatedType()")
public void method() {}
//在帶有DebugLog註解的構造方法
@Pointcut("execution(@com.example.aoplib.DebugLog *.new(..)) || constructorInsideAnnotatedType()")
public void constructor() {}
....
}
上面程式碼的註釋中,已經說明了每個PointCut的意義,也就是說,AspectJ會去尋找帶有@DebugLog註解的類,或者帶有@DebugLog註解的方法和構造方法。
接著是定義Advice
@Around("method() || constructor()")
public Object logAndExecute(ProceedingJoinPoint joinPoint) throws Throwable {
//執行方法前,做些什麼
enterMethod(joinPoint);
long startNanos = System.nanoTime();
//執行原方法
Object result = joinPoint.proceed();
long stopNanos = System.nanoTime();
long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);
//執行方法後,做些什麼
exitMethod(joinPoint, result, lengthMillis);
return result;
}
這些程式碼,和Hugo中的一模一樣,我將其抽取,構成一個簡單的library,叫做aoplib,看下圖:
Gradle
從上圖可以看到,buildsrc中,包含了我自定義的一個gradle外掛,其內容和Hugo專案中的hugo-plugin一致。
app或者lib需要新增ajc編譯的內容,只需要apply這個外掛就可以了,不必每個模組都編寫相同的gradle程式碼。
apply plugin: ‘com.hc.gradle’
測試
定義Aop以後,我們可以在app資料夾下,也就是我們的Android專案中使用,並且,我們也可以在自己建立的module中使用,如上圖,testlib就是我建立的一個test模組,它依賴aoplib,因此可以使用aop定義的註解,並且使用了我自定義的外掛,也不需要自己寫Ajc部分內容。
這個test模組中,我對AspectJ的使用範圍作了一些測試,有興趣的朋友可以看看。
寫在最後
最後貼上專案程式碼AopDemo,歡迎大家fork和star,本專案程式碼可以看出對Hugo的梳理,如果對Hugo程式碼感到閱讀困難,可以檢視我自認為更加清晰的程式碼。