WMRouter V1.2.0 源码简要分析

简介

源码结构

参考博客:https://www.jianshu.com/p/dd9a2ad6995d

  • compiler:处理interfaces定义的注解,生成相应的源文件。
  • interfaces: 定义了五种注解,分别是RouterPager、RouterRegex、RouterUri、RouterProvider、RouterService。
  • plugin:自定义一个名为WMRouter 的gradle插件,将注解生成器生成的初始化类汇总到ServiceLoaderInit,运行时直接调用ServiceLoaderInit
  • router:核心库

常见的路由框架

  • Arouter
  • WMRouter
  • Component Caller
  • ModularizationArchitecture

核心思想

SPI

SPI(Service Provider Interface),是java提供的一套用来被第三方实现或者扩展的API。它可以用来启用框架扩展和替换组件。Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

使用步骤:

1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;

2、接口实现类所在的jar包放在主程序的classpath中;

3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;

4、SPI的实现类必须携带一个不带参数的构造方法;

ServiceLoader

  • java提供的ServiceLoader。原理是根据传入的接口类,遍历META-INF/services目录下的以该类命名的文件中的所有类,并实例化返回。通过返回一个Iterator对象能够做到对服务实例的懒加载。

缺点:

  • ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。

ServiceLoader模块

WMRouter的Service模块实现了java提供的ServiceLoader功能。并有以下特点:

  • 使用注解自动配置
  • 支持获取接口的所有实现,或根据Key获取特定实现
  • 支持获取Class或获取实例
  • 支持无参构造、Context构造,或自定义Factory、Provider构造
  • 支持单例管理
  • 支持方法调用

interfaces

  • 提供注解,四个编译时注解RouterPage,RouterRegex,RouterService,RouterUri。一个运行时注解RouterProvider。
  • 提供框架常量
  • 提供Service的一个实现ServiceImpl。

compiler

compiler主要功能是处理interfaces自定义的注解,生成相应的class文件。使用的技术编译时注解APT,javapoet,AutoService。

  • APT(Annotation Process Tool)是一种在代码编译时处理注解,并按照一定的规则,生成java文件。
  • javapoet 是一款可以自动生成Java文件的第三方依赖。
  • AutoService 会自动在META-INF文件夹下生成Processor配置信息文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定,方便快捷

依赖包

compile 'com.google.auto.service:auto-service:1.0-rc2'
compile 'com.squareup:javapoet:1.9.0'

源码

  • BaseProcessor 是其他注解处理器的基类,用于获取主机处理工具框的基本元素、被注解元素的判断,生成java文件。
public abstract class BaseProcessor extends AbstractProcessor{
	/**创建源文件,类的辅助文件的filer**/
	protected Filer filer;
	/**对类型操作的辅助方法**/
    protected Types types;
    /**对元素操作的辅助方法**/
    protected Elements elements;
}

注意

  • Elements 是代表程序的一个元素,这个元素可以是包,类/接口,属性变量,方法,方法形参,泛型参数。每个元素表示一个静态的语言级构造。ElementKind这个描述了java源文件中的所有文件结构。
  • Types 用来处理TypeMirror的工具类。
  • TypeMirror 类中有一个最重要的枚举TypeKind。TypeKind这个描述了Java语法的所有类型。
  • Elements 和Types的区别:Elements 是获取Java文件的静态结构。Types 是获取静态结构的语法含义。
注解处理器源码ServiceAnnotationProcessor
@AutoService(Processor.class)//在META-INF生成相应的配置
@SupportedSourceVersion(SourceVersion.RELEASE_7)// 支持的java版本
public class ServiceAnnotationProcessor extends BaseProcessor{
    //用于存储注解RouterService的属性值
    // Entity最为重要的是ServiceImpl这个类。参见源码
    private final HashMap<String, Entity> mEntityMap = new HashMap<>();
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
		// 判断是否解析完成
        if (env.processingOver()) {
            //生成文件
            generateInitClass();
        } else {
        	//处理注解过程
            processAnnotations(env);
        }
        return true;
    }
    private void processAnnotations(RoundEnvironment env){
        // 获取所有被RouterService注解的元素
        for (Element element : env.getElementsAnnotatedWith(RouterService.class)){
            // 判断被注解的元素是不是类,如果不是类则不进行处理
            if (!(element instanceof Symbol.ClassSymbol)) {
                continue;
            }
            ...
            // 获取类的注解,也就是获取RouterService注解
            RouterService service = cls.getAnnotation(RouterService.class);
            //获取RouterService 注解里面的 interfaces 属性
            List<? extends TypeMirror> typeMirrors = getInterface(service);
            //获取RouterService 注解里面的 key 属性
            String[] keys = service.key();
            // 获取类名
            String implementationName = cls.className();
            // 获取RouterService 注解里面的 singleton 属性
            boolean singleton = service.singleton();
            ... 
            /**
            *将注解的数据存储为Entity。并存储在HashMap<String, Entity> mEntityMap中,为生成文件做准备
            **/
        }
    }
}
public static class Entity {
      private final String mInterfaceName;
        /**
         * key 为 RouterService --- key
         * value 为 RouterService --- 整个注解属性
         */
        private final Map<String, ServiceImpl> mMap = new HashMap<>();
}
public class ServiceImpl{
    /**
     * 注解的key
     */
    private final String key;
    /**
     * 注解interfaces 实现的接口或者继承的父类
     */
    private final String implementation;
    /**
     * 被注解的类
     */
    private final Class implementationClazz;
    /**
     * 是否为单例
     */
    private final boolean singleton;
}

注意

  • 从源码可知注解RouterService的key是获取实现类的唯一标识,在整个工程中必须是唯一的。如果出现不唯一编译过程中会报错。

生成代码部分

public class ServiceInit_md5 {
    public ServiceInit_md5() {
    }

    public static void init() {
        ServiceLoader.put("注解中的interface类", "key字符串", "被注解的类", "单例标识");
    }
}
注解处理器UriAnnotationProcessor
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class UriAnnotationProcessor extends BaseProcessor{
        @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env){
        // step1 :获取被RouterUri注解的类
        // 生成相应的文件
    }
}
// 注意和ServiceAnnotationProcessor不同点是:生成两个文件
public void buildHandlerInitClass(CodeBlock code, String genClassName, String handlerClassName, String interfaceName) {
    ...
    // 生成UriAnnotationInit_xxx文件
    try {
            JavaFile.builder(Const.GEN_PKG, typeSpec)
                    .build()
                    .writeTo(filer);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } 
    ...
    // 生成ServiceInit_xx文件
    new ServiceInitClassBuilder(className)
                .putDirectly(interfaceName, fullImplName, fullImplName, false)
                .build();
}

生成的文件举例

public class UriAnnotationInit_xx implements IUriAnnotationInit {
    public UriAnnotationInit_xx() {
    }
    public void init(UriAnnotationHandler handler) {
        handler.register("scheme", "host", "path", "被注解的类", "外部跳转标志", "拦截器数组");
   		...     
    }
}
public class ServiceInit_xx {
    public ServiceInit_xx() {
    }

    public static void init() {
        ServiceLoader.put(IUriAnnotationInit.class, "UriAnnotationInit_xx", UriAnnotationInit_xx.class, false);
    }
}

注解处理器PageAnnotationProcessor

和注解处理UriAnnotationProcessor处理流程相似

注解处理器RegexAnnotationProcessor

和注解处理UriAnnotationProcessor处理流程相似

注解处理器PageAnnotationProcessor

和注解处理UriAnnotationProcessor处理流程相似

plugin

该模块一是提供编译调试时的日志。二是根据compiler生成的文件自动生成文件ServiceLoaderInit.class。

重点描述一下生成文件ServiceLoaderInit.class。该方式使用到Transform API。Transform用于在编译打包的.class文件到.dex文件流程中,去转换.class文件。

public void transform(TransformInvocation invocation){
    // 遍历所有的文件
    for (TransformInput input : invocation.getInputs()){
        
    }
    // 生成ServiceLoaderInit.class文件
        /**
     * 生成格式如下的代码,其中ServiceInit_xxx由注解生成器生成。
     * <pre>
     * package com.sankuai.waimai.router.generated;
     *
     * public class ServiceLoaderInit {
     *
     *     public static void init() {
     *         ServiceInit_xxx1.init();
     *         ServiceInit_xxx2.init();
     *     }
     * }
     * </pre>
     */
    generateServiceInitClass(dest.getAbsolutePath(), initClasses);
}

router

router主要分为两块内容:1,ServiceLoader模块;2,UriHandler

  1. SeriviceLoader模块的主要功能
    • 通过反射调用ServiceLoaderInit(在plugin生成)的init方法。将注解生成的配置文件,相关的实现类保存在map中。map分为两个一个是HashMap<String, ServiceImpl> mMap = new HashMap<>();另一是Map<Class, ServiceLoader> SERVICES = new HashMap<>();这部分主要是为了使用时加载相关的实现类。
    • 通过get方法获取实例。方式有无参,context,自己实现的IFactory,注解RouterProvider。
    • 单例存储在SigletonPool,Provider缓存到ProviderPool
 private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
        @Override
        protected void doInit() {
            try {
                // 反射调用Init类,避免引用的类过多,导致main dex capacity exceeded问题
                /**
                 * 使用反射调用
                 * ServiceLoaderInit 的 init方法
                 */
                Class.forName(Const.SERVICE_LOADER_INIT)
                        .getMethod(Const.INIT_METHOD)
                        .invoke(null);
                Debugger.i("[ServiceLoader] init class invoked");
            } catch (Exception e) {
                Debugger.fatal(e);
            }
        }
    };
    //  缓存相关实现类
      public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
        Log.d(TAG,"interfaceClass name = "+interfaceClass.getName());
        ServiceLoader loader = SERVICES.get(interfaceClass);
        if (loader == null) {
            loader = new ServiceLoader(interfaceClass);
            SERVICES.put(interfaceClass, loader);
        }
        loader.putImpl(key, implementClass, singleton);
    }
  1. UriHandler的主要功能是处理最终的实现。UriRequest用于存储路由的地方放和响应放相互传递的数据。UriInterceptor的作用是在UriHandler处理前的预先处理,是UriHandler的拦截器,处理方式和OKhttp拦截器的处理方式相同。UriCallback是当前Handler的处理结果回调,可以是继续分法处理,也可以是处理完成。

  2. UriHandler具有层次结构。层次结构,推荐博客http://www.hchstudio.cn/article/2018/e164/和官方文档。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值