1. 程式人生 > >使用AspectJ在Android中實現Aop

使用AspectJ在Android中實現Aop

開題

上一篇文章Android Aop預研中介紹了Aop的各種實現方式,並且在最後提到,選擇AspectJ作為合適的開發方式。這篇文章通過我自己編寫的一個例子,來說明AspectJ的使用。

可以這樣說,上面的文章將我所要介紹的大部分關於AOP的內容都包含了,而且講解得很明晰易懂,建議在閱讀本文之前,先讀上面的文章。

另外,本文不對AspectJ相關的基本語法做介紹,在閱讀本文前,最好對AspectJ有基本的瞭解,由於網上的朋友,對這些內容有大量的介紹,我也不去做這些重複的工作。推薦文章為深入理解Android之AOP

最後,還需要提醒大家,本文專案程式碼中,使用到自定義gradle外掛的相關知識,這裡也不贅述,推薦文章為

在 Android Studio 中自定義 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程式碼感到閱讀困難,可以檢視我自認為更加清晰的程式碼。