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

作者: ZJ_Rocky | 来源:发表于2017-10-23 12:14 被阅读84次

    主目录见: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使用例子也就结束了,步骤还是非常简单的。这个思想还能干很多牛逼的事情,希望大家想不到解决方案的时候,这可以当做一个备选。
    总结:今天的例子是非常简单的,因为这个库用起来本来就比较容易,但是思想还是非常棒的,如果大家有兴趣希望进一步了解,看看我上面推荐的几篇文章。

    相关文章

      网友评论

        本文标题:流行框架源码分析(10)-AOP在代码中的使用分析

        本文链接:https://www.haomeiwen.com/subject/gzmouxtx.html