简介
- git路径:https://github.com/meituan/WMRouter.git
- 官方文档介绍:https://tech.meituan.com/2018/08/23/meituan-waimai-android-open-source-routing-framework.html
源码结构
参考博客: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
- 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);
}
-
UriHandler的主要功能是处理最终的实现。UriRequest用于存储路由的地方放和响应放相互传递的数据。UriInterceptor的作用是在UriHandler处理前的预先处理,是UriHandler的拦截器,处理方式和OKhttp拦截器的处理方式相同。UriCallback是当前Handler的处理结果回调,可以是继续分法处理,也可以是处理完成。
-
UriHandler具有层次结构。层次结构,推荐博客http://www.hchstudio.cn/article/2018/e164/和官方文档。