流行框架源碼分析(10)-AOP在代碼中的使用分析

主目錄見:Android高級進階知識(這是總目錄索引)
?AOP(Aspect-oriented programming)面向切面編程,如果你關注到這個概念的話,那你應該大概懂得他的作用了。最早接觸這個概念是在Spring框架中,如今android也越來越多地地方可以使用他了,其實他不是一門語言,他是一種編程思想。今天我們不講基礎的概念,如果你還不了解,這里推薦幾篇文章:
1.Android中的AOP編程
2.Aspect Oriented Programming in Android這是前一篇的英文版本;
3.深入理解Android之AOP
4.同時這里推薦一個項目hugo (一個日志打印框架)。

一.目標

AOP的實現框架有好多,我們今天選擇一個AOP庫AspectJ來實現我們的功能,我們今天還是以我們的框架LRouter來講解。今天有如下目標:
1.了解AOP的思想概念;
2.懂得簡單地AOP庫AspectJ的使用。

二.使用講解

首先講解之前我來說一下為什么當初會用到這個,因為當初寫這個框架有個需求是:

因為框架是多組件跨進程的模塊分割的,然后我們想知道各個模塊之間的通訊頻率,還有哪個模塊被訪問的次數最多呢?這時候我們不能再對原有的框架做一個大的改動,像ARouter用的是CountDownLatch機制來做的攔截。但是我們這里明顯不適用,我們只能選擇一個侵入性比較小的辦法,那就是這里的AOP了,我們在跨進程請求的部分做了一個攔截,攔截了請求參數,請求的方法等等信息來做一個記錄。

1.gradle文件的配置

我們的AspectJ庫必須使用 AspectJ 的編譯器(ajc,一個java編譯器的擴展)對所有受 aspect 影響的類進行織入。所以我們需要一些配置,在hugo中這個配置是寫成gradle插件了。我們這里就直接添加到LRouter項目中的lrouter-api項目的gradle中:


gradle文件

具體配置內容在前面文章和項目里面有。

2.Aspect(切面)的選擇

我們知道,我們跨進程或者跨模塊的訪問是在lrouter-api模塊的LocalRouter類中的navigation()方法進行訪問的,所以我們可以攔截這個方法的訪問,我們選擇跟hugo同樣的做法,在這個方法上面添加一個注解@Navigation:

    @Navigation
public ListenerFutureTask navigation(Context context, LRouterRequest request) throws Exception {
........
}

然后我們就可以來定義Pointcut切入點了。

  //篩選出用Navigation注解的所有方法
    private static final String POINTCUT_METHOD = "execution(@com.lenovohit.lrouter_api.intercept.ioc.Navigation * *(..))";
    //篩選出所有用Navigation注解的所有構造函數
    private static final String POINTCUT_CONSTRUCTOR = "execution(@com.lenovohit.lrouter_api.intercept.ioc.Navigation *.new(..))";

    @Pointcut(POINTCUT_METHOD)
    public void methodAnnotationWithNavigation(){}

    @Pointcut(POINTCUT_CONSTRUCTOR)
    public void constructorAnnotaionWithNavigation(){}

可以看到我們選擇了添加了Navigation注解的所有方法和構造函數作為切入點。后面我們就可以選擇合適的Advice了,我們這里選擇Around這個類型。

 @Around("methodAnnotationWithNavigation() || constructorAnnotaionWithNavigation()")
    public Object requestAndExecute(ProceedingJoinPoint joinPoint) throws Throwable{
        //執行方法前
        enterRequestMethod(joinPoint);
        //方法執行
        long startNanos = System.nanoTime();
        Object result = joinPoint.proceed();
        long stopNanos = System.nanoTime();
        long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);
        //執行方法后
        exitRequestMethod(joinPoint,lengthMillis);

        return result;
    }

我們看到這里我們定義了Around類型的Advice,然后我們在方法里在攔截的方法前執行了enterRequestMethod(),還有在方法執行后調用了exitRequestMethod()方法,我們來看下:

public  void enterRequestMethod(JoinPoint joinPoint){
        CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();

        String methodName = codeSignature.getName();
        String[] parameterNames = codeSignature.getParameterNames();
        Object[] parameterValues = joinPoint.getArgs();

        if (null != mAopInterceptors) {
            for (AopInterceptor aopInterceptor : mAopInterceptors){
                aopInterceptor.enterRequestIntercept(methodName, parameterNames, parameterValues);
            }
        }
    }

我們看到這個方法里面通過JoinPoint獲取到了方法名,方法參數,參數值等信息,然后調用我們自己的方法進行處理。我們這里是用一個抽象方法來實現,意思就是說給用戶自己來處理:

public abstract class AopInterceptor {
    /**
     * 進入請求方法前
     * */
    public abstract void enterRequestIntercept(String methodName,String[] paramNames,Object[] paramValues);
    /**
     * 執行完請求方法后
     * */
    public abstract  void exitRequestIntercept(String methodName,String[] paramNames,Object[] paramValues,long lengthMillis);
}

到這里我們的Aop使用例子也就結束了,步驟還是非常簡單的。這個思想還能干很多牛逼的事情,希望大家想不到解決方案的時候,這可以當做一個備選。
總結:今天的例子是非常簡單的,因為這個庫用起來本來就比較容易,但是思想還是非常棒的,如果大家有興趣希望進一步了解,看看我上面推薦的幾篇文章。

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

推薦閱讀更多精彩內容