AspectJ基本用法

參考:深入理解Android之AOP

AOP雖然是方法論,但就好像OOP中的Java一樣,一些先行者也開發(fā)了一套語言來支持AOP。目前用得比較火的就是AspectJ了,它是一種幾乎和Java完全一樣的語言,而且完全兼容Java(AspectJ應(yīng)該就是一種擴(kuò)展Java,但它不是像Groovy[[1]](file:///E:/Android-Internals/%E4%B8%93%E9%A2%98%E5%8D%B7/%E9%9A%8F%E7%AC%94%E7%B3%BB%E5%88%97/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Android%E4%B9%8BAOP.docx#_ftn1)那樣的拓展。)。當(dāng)然,除了使用AspectJ特殊的語言外,AspectJ還支持原生的Java,只要加上對應(yīng)的AspectJ注解就好。所以,使用AspectJ有兩種方法:

  • 完全使用AspectJ的語言。這語言一點(diǎn)也不難,和Java幾乎一樣,也能在AspectJ中調(diào)用Java的任何類庫。AspectJ只是多了一些關(guān)鍵詞罷了。
  • 或者使用純Java語言開發(fā),然后使用AspectJ注解,簡稱@AspectJ

AspectJ的配置可以參考另一篇文章Android中使用AspectJ

Join Points介紹

Join Points是AspectJ中的一個關(guān)鍵概念。Join Points可以看作是程序運(yùn)行時的一個執(zhí)行點(diǎn),比如:一個函數(shù)的調(diào)用可以看作是個Join Points,如Log.e()這個函數(shù),e()可以看作是個Join Points,而調(diào)運(yùn)e()的函數(shù)也可以認(rèn)為是一個Join Points;設(shè)置一個變量,或者讀取一個變量也可以是個Join Points;for循環(huán)也可以看作是Join Points。

理論上說,一個程序中很多地方都可以被看做是Join Points,但是AspectJ中,只有下面所示的幾種執(zhí)行點(diǎn)被認(rèn)為是Join Points:

Join Points 說明 示例
method call 函數(shù)調(diào)用 比如調(diào)用Log.e(),這是一處JPoint
method execution 函數(shù)執(zhí)行 比如Log.e()的執(zhí)行內(nèi)部,是一處Join Points。注意它和method call的區(qū)別。method call是調(diào)用某個函數(shù)的地方。而execution是某個函數(shù)執(zhí)行的內(nèi)部。
constructor call 構(gòu)造函數(shù)調(diào)用 和method call類似
constructor execution 構(gòu)造函數(shù)執(zhí)行 和method execution類似
field get 獲取某個變量 比如讀取DemoActivity.debug成員
field set 設(shè)置某個變量 比如設(shè)置DemoActivity.debug成員
pre-initialization Object在構(gòu)造函數(shù)中做得一些工作。
initialization Object在構(gòu)造函數(shù)中做得工作
static initialization 類初始化 比如類的static{}
handler 異常處理 比如try catch(xxx)中,對應(yīng)catch內(nèi)的執(zhí)行
advice execution 這個是AspectJ的內(nèi)容,稍后再說

這里列出了AspectJ所認(rèn)可的JoinPoints的類型。實際上,也就是你想把新的代碼插在程序的哪個地方,是插在構(gòu)造方法中,還是插在某個方法調(diào)用前,或者是插在某個方法中,這個地方就是Join Points,當(dāng)然,不是所有地方都能給你插的,只有能插的地方,才叫Join Points。

Pointcuts介紹

一個程序會有多個Join Points,即使同一個函數(shù),也還分為call和execution類型的Join Points,但并不是所有的Join Points都是我們關(guān)心的,Pointcuts就是提供一種使得開發(fā)者能夠選擇自己需要的JoinPoints的方法。

Advice

Advice就是我們插入的代碼以何種方式插入,有Before還有After、Around。

看個例子

@Before("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
}

這里會分成幾個部分,我們依次來看:

  • @Before:Advice,也就是具體的插入點(diǎn)
  • execution:處理Join Point的類型,例如call、execution
  • (* android.app.Activity.on**(..)):這個是最重要的表達(dá)式,第一個*表示返回值,*表示返回值為任意類型,后面這個就是典型的包名路徑,其中可以包含 * 來進(jìn)行通配,幾個 * 沒區(qū)別。同時,這里可以通過&&、||、!來進(jìn)行條件組合。()代表這個方法的參數(shù),你可以指定類型,例如android.os.Bundle,或者(..)這樣來代表任意類型、任意個數(shù)的參數(shù)。
  • public void onActivityMethodBefore:實際切入的代碼。

Before和After其實還是很好理解的,也就是在Pointcuts之前和之后,插入代碼,那么Around呢,從字面含義上來講,也就是在方法前后各插入代碼,是的,他包含了Before和After的全部功能,代碼如下:

@Around("execution(* com.xys.aspectjxdemo.MainActivity.testAOP())")
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    String key = proceedingJoinPoint.getSignature().toString();
    Log.d(TAG, "onActivityMethodAroundFirst: " + key);
    proceedingJoinPoint.proceed();
    Log.d(TAG, "onActivityMethodAroundSecond: " + key);
}

其中,proceedingJoinPoint.proceed()代表執(zhí)行原始的方法,在這之前、之后,都可以進(jìn)行各種邏輯處理。

自定義Pointcuts

自定義Pointcuts可以讓我們更加精確的切入一個或多個指定的切入點(diǎn)。

首先我們要定義一個注解類

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}

在需要插入代碼的地方加入這個注解。如在MainActivity中加入,

public class MainActivity extends AppCompatActivity {
    final String TAG = MainActivity.class.getSimpleName();

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

    @DebugTrace
    public void logTest() {
        Log.e(TAG, "log test");
    }
}

最后,創(chuàng)建切入代碼

@Pointcut("execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))")
public void DebugTraceMethod() {}

@Before("DebugTraceMethod()")
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable {
    String key = joinPoint.getSignature().toString();
    Log.e(TAG, "beforeDebugTraceMethod: " + key);
}

log如下

E22F03D2-C333-4A4C-9C7E-3DE97F221375.png

在AspectJ的切入點(diǎn)表達(dá)式中,我們前面都是使用的execution,實際上,還有一種類型——call,那么這兩種語法有什么區(qū)別呢,對于Call來說:

Call(Before)
Pointcut{
    Pointcut Method
}
Call(After)

對于Execution來說:

Pointcut{
  execution(Before)
    Pointcut Method
  execution(After)
}

withincode

這個語法通常來進(jìn)行一些切入點(diǎn)條件的過濾,作更加精確的切入控制。如下

public class MainActivity extends AppCompatActivity {
    final String TAG = MainActivity.class.getSimpleName();

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

    public void aspectJTest() {
        Log.e(TAG, "execute aspectJTest");
    }

    public void aspectJ1(){
        aspectJTest();
    }

    public void aspectJ2(){
        aspectJTest();
    }

    public void aspectJ3(){
        aspectJTest();
    }
}

aspectJ1(),aspectJ2(),aspectJ3()都調(diào)用了aspectJTest方法,但只想在aspectJ2調(diào)用aspectJTest時插入代碼,這個時候就需要使用到Pointcut和withincode組合的方式,來精確定位切入點(diǎn)。

@Pointcut("(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())")
public void invokeAspectJTestInAspectJ2() {
}

@Before("invokeAspectJTestInAspectJ2()")
public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable {
    Log.e(TAG, "method:" + getMethodName(joinPoint).getName());
}

private MethodSignature getMethodName(JoinPoint joinPoint) {
    if (joinPoint == null) return null;
    return (MethodSignature) joinPoint.getSignature();
}

log如下

04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest
04-02 23:44:40.681 12107-12107/ E/AspectTest: method:aspectJTest
04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest
04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest

以上就是Aspecj的基本使用方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 看AspectJ在Android中的強(qiáng)勢插入 什么是AOP AOP是Aspect Oriented Program...
    eclipse_xu閱讀 11,682評論 11 84
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • AOP之AspectJ 前言:這幾天一直在學(xué)習(xí)aop切面編程,以前一直也有聽過aop但是實際用的還是比較少,不是很...
    六_六閱讀 1,790評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,532評論 25 708
  • 昨天打電話回家,母親說萍表姐死了,當(dāng)時心中不禁抽搐了一下,雖然知道每個人都會死是不變的自然規(guī)律,但對于萍表姐來說,...
    藍(lán)色海浪閱讀 338評論 0 0