AOP之aspectj

AOP之aspectj

aop,英文全稱爲Aspect Oriented Programming,意思是面向切面編程,是一種高內聚,低耦合的編程思想。在很多業務中都有廣泛的應用。

業務場景

有這麼一個需求,要統計activity中的oncreate方法耗時,爲後續做卡頓優化提供數據支撐的基礎。

  • 一般實現,在每個activity的oncreate方法中添加統計耗時代碼,比如
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        long start = System.currentTimeMillis();//1
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, " spend time =" + (System.currentTimeMillis() - start));//2

    }

想象一下,每個activity中的oncreate方法都多出1,2步驟的代碼,如果是所有activity,所有的生命週期都要統計呢,是不是感覺這個工作很繁瑣,並且重複,無意義。程序員是比較懶的一羣生物,他會想辦法偷懶,所以,aspectj這時候就出現了。

有aspectj後的業務代碼

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
    }

如果有了aspectj技術以後,那麼業務程序員還是一如既往的正常編寫業務代碼,無需添加任何統計代碼,那麼統計的需求如何實現呢。爲了讓例子更簡單易懂,我們這裏還是隻統計oncreate,避免代碼太多,所帶來的迷糊困惑

aspectj

引入

偉大的開源社區其實有非常好的aspectj的sdk,這裏我推薦hujiang,開源地址https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
該項目是在註解的基礎上實現aop的,記住是註解,這是他的巨大優勢(易上手),同時也有點缺陷(有一定的業務代碼入侵)

開始

代碼工程地址demo

  • 首先在項目工程的build.gradle中引入配置
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
  • 然後再在app中的build.gradle中引入
implementation "org.aspectj:aspectjrt:1.8.9"
apply plugin: 'android-aspectjx'
aspectjx {
    include 'com.example'
}

然後就可以開始愉快的編寫aop代碼了

aspectj代碼

@Aspect
public class SpendTimeAspect {
    @Pointcut("execution(* *.onCreate(..))")
    public void injectSpendCode() {
        //生命所有的類中oncreate方法,作爲切入點
    }

    @Around("injectSpendCode()")
    public void aroundOnCreate(ProceedingJoinPoint joinPoint) throws Throwable {
        //插入的代碼
        long start = System.currentTimeMillis();
        //執行原來的代碼
        Object[] params = joinPoint.getArgs();
        joinPoint.proceed(params);
        //插入的代碼
        Log.d("SpendTimeAspect","method spend "+(System.currentTimeMillis()-start));
    }

}

講解一下代碼步驟

  1. 首先新建一個Java,名字自己取就好,這裏是SpendTimeAspect
  2. 使用註解@Aspect來聲明類,這裏的作用是告訴編譯器,這個類要插樁行爲
  3. 使用註解@Pointcut來聲明哪個類哪個方法要進行插樁,規則匹配支持通配符。其中*.onCreate(..)的意思就是所有類的oncreate方法要進行插樁,該方法無需任何實現
  4. 使用註解@Around來聲明要插入的代碼,就像以前一樣正常編寫java,這裏的代碼都會被插樁過去。

代碼效果大家可以拷貝工程下來運行看一下,有興趣研究aspectj源碼的同學,可以繼續深究一下,你會發現aspectj的實現也是asm來實現的,所以asm是個更加強大的aop實現技術方案,只不過入門門檻高。後續也會有文章來講解這個,然後hujiang這個插件在aspectj基礎上,配合google的transform流程

aspectj註解語法

其實,大多數業務情況,掌握我上面所用到的例子中的語法就可以完成很多很多黑科技了。本着文章的完整性,還是系統性講一次語法相關的吧。

  • @Aspect 用它聲明一個類,表示一個需要執行的切面。
  • @Pointcut 聲明一個切點。
  • @Before/@After/@Around/@AfterReturning/@AfterThrowing(統稱爲Advice類型) 聲明切面代碼的執行順序,建議使用around,可以滿足大部分需求

@Aspect

直接聲明一個類,這樣子纔會執行這個類裏面的插樁語法

@Pointcut

指定了一個代碼織入點,註解內的execution(* .onCreate(..))是一個切點表達式,第一個號表示返回值可爲任意類型,後跟包名+類名+方法名(可以用*代表全匹配的意思),括號內表示參數列表, .. 表示匹配任意個參數

  • Method call:方法被調用
  • Method execution:方法執行
  • Constructor call:構造函數被調用
  • Constructor execution:構造函數執行
  • Static initialization:static 塊初始化
  • Field get:讀取屬性
  • Field set:寫入屬性
  • Handler:異常處理

@Advice

  • @Before:切入點前織入
  • @After:切入點後織入,無論連接點執行如何,包括正常的 return 和 throw 異常
  • @AfterReturning:只有在切入點正常返回之後纔會執行,不指定返回類型時匹配所有類型
  • @AfterThrowing:只有在切入點拋出異常後才執行,不指定異常類型時匹配所有類型
  • @Around:替代原有切點,如果要執行原來代碼的話,調用 ProceedingJoinPoint.proceed()

語法大全

語法手冊

總結

aspectJ是一個容易上手來實現aop的技術方案,趕緊動起小手,自己一步一步的寫個demo調試一下。也歡迎在我的demo基礎上,動手實驗.


希望我的文字能給你帶來幫助,同時也歡迎關注個人公衆號,我的所有文章第一手信息回優先發布這裏。
個人公衆號:河邊的小黑屋

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