ARouter 学习-源码分析如何暴露服务
2018-01-31 17:57
821 查看
Arouter提供自定义注解Route方便应用去标注哪些服务可以暴露给其它模块。
标注了就可以暴露?是的,不过是依靠自定义注解处理器来实现的,下面开始分析arouter-compiler中专门用来处理Route的处理器 RouteProcessor。
一个自定义注解处理器大致的代码结构如下:
重点需要关注init和process方法。
RouterProcessor init方法
人如起名,init做的就是初始化操作,作者也备注了重要的几个参数,就不说了。进入process方法。
获取到所有被Route注解过得Element. 然后调用parseRoutes方法解析Route.
parseRoutes方法较长,分段来分析。
第一段
Element->表示一个程序元素,比如包,类或者方法。
TypeElement-> 表示一个类或者接口程序元素
TypeMirror->表示Java编程语言中的类型
举个例子:后面编织代码时会判断当前被注解的类是不是Activity,此时会用到type_Activity来做类型判断:
types.isSubtype(tm, type_Activity)
第一段主要是创建后面编织代码时会用到的TypeElement和TypeMirror.
那ClassName是什么???这里需要了解JavaPoet, javaPoet是用来帮助我们生成Java源码的工具。
TypeSpec 代表一个类,接口,或者枚举声明
FieldSpec 代表一个成员变量,一个字段声明。
JavaFile包含一个顶级类的Java文件。
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
TypeName 类型,如在添加返回值类型是使用 TypeName.VOID
ClassName 用来包装一个类
beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
addCode() 以字符串的形式添加内
returns 添加返回值类型
.constructorBuilder() 生成构造器函数
.addAnnotation 添加注解
addSuperinterface 给类添加实现的接口
superclass 给类添加继承的父类
ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
MethodSpec.constructorBuilder() 构造器
addTypeVariable(TypeVariableName.get(“T”, typeClassName))
具体如何使用不展开,结合Arouter是如何编制代码来分析。
第二段
上面代码主要是利用JavaPoet创建参数和loadInfo方法,生成代码如下:
public void loadInto(Map<String, Class<?extends IRouteGroup>> routes) {
}
第三段
因我们的目的主要是分析跨模块API调用,这里只需要关注
if (types.isSubtype(tm,iProvider)) {// IProvider
logger.info(">>> Found provider route: " + tm.toString() +" <<<");
routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
}
发现是IProvider子类型后,将Route和element相关信息保存到RouteMeta结构体中。
routeMete信息会在categorites里排序后,按照组信息被加到:
private Map<String, Set<RouteMeta>>groupMap =new HashMap<>();//
ModuleName and routeMeta.
第四段
Start generate java source.
4.1 第一部分生成方法:
public void loadInto(Map<String, RouteMeta> providers) {}
4.2 第二部分遍历groupMap:
先生成方法:
public void loadInto(Map<String, RouteMeta> atlas) {
}
然后遍历单个组内的RouteMeta信息:
for (RouteMeta routeMeta : groupData) {
switch (routeMeta.getType()) {
case PROVIDER:
// handle provider case.
}
}
重点关注如何处理Provider case.
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
这里是拿到被Route注解的类实现了哪些接口 。
for (TypeMirror tm : interfaces) {
if (types.isSameType(tm, iProvider)) {
..................
}else if(types.isSubtype(tm,iProvider)){
..................
}
}
遍历interfaces,判断interface是自己实现了IProvider还是interface继承了IProvider.为什么要加两个判断?没有看太明白.
在ARouter\$\$Providers\$\$app类loadInfo方法中添加:
providers.put("com.router.service.IService", RouteMeta.build(RouteType.PROVIDER,
MyService.class,
"/service/hello",
"service", null, -1, -2147483648));
在ARouter\$\$Group\$\$service类loadInfo方法中添加:
atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, MyService.class,"/service/hello","service",
null, -1, -2147483648));
4.3 第三部分生成Group java source file
将4.1和4.2生成的方法,写入以groupFileName命名的文件中。最终生成的类如下:
第五段
最后生成两个类:
和
注解处理器处理Route分析完毕。
一个Route注解,经过自定义RouteProcessor的处理,最终生成三个类。那这些类有什么用呢???接下来分析arouter-api是如何做到块模块调用到暴露的服务。
** * Mark a page can be route by router. * * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a> * @version 1.0 * @since 16/8/15 下午9:29 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.CLASS) public @interface Route { /** * Path of route */ String path(); /** * Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!! */ String group() default "";
标注了就可以暴露?是的,不过是依靠自定义注解处理器来实现的,下面开始分析arouter-compiler中专门用来处理Route的处理器 RouteProcessor。
一个自定义注解处理器大致的代码结构如下:
public class ProcessorA extends AbstractProcessor { //这个方法主要是获取工具类 public synchronized void init(ProcessingEnvironment env){ } //这个方法里面写处理的过程,输入参数annotations是getSupportedAnnotationTypes()的子集,即是工程代码中所有注解集合与getSupportedAnnotationTypes()的交集,RoundEnvironment env代表这一轮扫描后的结果,返回true则表示消费完此次扫描,此轮扫描注解结束 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { } //返回需要处理的注解的类的getCanonicalName()集合 public Set<String> getSupportedAnnotationTypes() { } //返回SourceVersion.latestSupported()即可 public SourceVersion getSupportedSourceVersion() { } }
重点需要关注init和process方法。
RouterProcessor init方法
public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); // Generate class. types = processingEnv.getTypeUtils(); // Get type utils. elements = processingEnv.getElementUtils(); // Get class meta. typeUtils = new TypeUtils(types, elements); logger = new Logger(processingEnv.getMessager()); // Package the log utils. // Attempt to get user configuration [moduleName] Map<String, String> options = processingEnv.getOptions(); if (MapUtils.isNotEmpty(options)) { moduleName = options.get(KEY_MODULE_NAME); } if (StringUtils.isNotEmpty(moduleName)) { moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", ""); logger.info("The user has configuration the module name, it was [" + moduleName + "]"); } else { logger.error("These no module name, at 'build.gradle', like :\n" + "apt {\n" + " arguments {\n" + " moduleName project.getName();\n" + " }\n" + "}\n"); throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log."); } iProvider = elements.getTypeElement(Consts.IPROVIDER).asType(); logger.info(">>> RouteProcessor init. <<<"); }
人如起名,init做的就是初始化操作,作者也备注了重要的几个参数,就不说了。进入process方法。
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (CollectionUtils.isNotEmpty(annotations)) { Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class); try { logger.info(">>> Found routes, start... <<<"); this.parseRoutes(routeElements); } catch (Exception e) { logger.error(e); } return true; } return false; }
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class)
获取到所有被Route注解过得Element. 然后调用parseRoutes方法解析Route.
parseRoutes方法较长,分段来分析。
第一段
private void parseRoutes(Set<? extends Element> routeElements) throws IOException { if (CollectionUtils.isNotEmpty(routeElements)) { // Perpare the type an so on. logger.info(">>> Found routes, size is " + routeElements.size() + " <<<"); rootMap.clear(); TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType(); logger.info(">>>print type_activity:"+type_Activity); TypeMirror type_Service = elements.getTypeElement(SERVICE).asType(); TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType(); TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType(); // Interface of ARouter TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP); TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP); ClassName routeMetaCn = ClassName.get(RouteMeta.class); ClassName routeTypeCn = ClassName.get(RouteType.class);
Element->表示一个程序元素,比如包,类或者方法。
TypeElement-> 表示一个类或者接口程序元素
TypeMirror->表示Java编程语言中的类型
举个例子:后面编织代码时会判断当前被注解的类是不是Activity,此时会用到type_Activity来做类型判断:
types.isSubtype(tm, type_Activity)
第一段主要是创建后面编织代码时会用到的TypeElement和TypeMirror.
那ClassName是什么???这里需要了解JavaPoet, javaPoet是用来帮助我们生成Java源码的工具。
JavaPoet几个常用的类
MethodSpec 代表一个构造函数或方法声明。TypeSpec 代表一个类,接口,或者枚举声明
FieldSpec 代表一个成员变量,一个字段声明。
JavaFile包含一个顶级类的Java文件。
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
TypeName 类型,如在添加返回值类型是使用 TypeName.VOID
ClassName 用来包装一个类
javapoet 常用的API
addStatement() 方法负责分号和换行beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
addCode() 以字符串的形式添加内
returns 添加返回值类型
.constructorBuilder() 生成构造器函数
.addAnnotation 添加注解
addSuperinterface 给类添加实现的接口
superclass 给类添加继承的父类
ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
MethodSpec.constructorBuilder() 构造器
addTypeVariable(TypeVariableName.get(“T”, typeClassName))
具体如何使用不展开,结合Arouter是如何编制代码来分析。
第二段
/* Build input type, format as : ```Map<String, Class<? extends IRouteGroup>>``` */ ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get( ClassName.get(Map.class), ClassName.get(String.class), ParameterizedTypeName.get( ClassName.get(Class.class), WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup)) ) ); /* ```Map<String, RouteMeta>``` */ ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get( ClassName.get(Map.class), ClassName.get(String.class), ClassName.get(RouteMeta.class) ); /* Build input param name. */ ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec! ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build(); ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build(); /* Build method : 'loadInto' */ MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(rootParamSpec);
上面代码主要是利用JavaPoet创建参数和loadInfo方法,生成代码如下:
public void loadInto(Map<String, Class<?extends IRouteGroup>> routes) {
}
第三段
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root. for (Element element : routeElements) { TypeMirror tm = element.asType(); Route route = element.getAnnotation(Route.class); RouteMeta routeMete = null; if (types.isSubtype(tm, type_Activity)) { // Activity logger.info(">>> Found activity route: " + tm.toString() + " <<<"); // Get all fields annotation by @Autowired Map<String, Integer> paramsType = new HashMap<>(); for (Element field : element.getEnclosedElements()) { if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) { // It must be field, then it has annotation, but it not be provider. Autowired paramConfig = field.getAnnotation(Autowired.class); paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), typeUtils.typeExchange(field)); } } routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType); } else if (types.isSubtype(tm, iProvider)) { // IProvider logger.info(">>> Found provider route: " + tm.toString() + " <<<"); routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null); } else if (types.isSubtype(tm, type_Service)) { // Service logger.info(">>> Found service route: " + tm.toString() + " <<<"); routeMete = new RouteMeta(route, element, RouteType.parse(SERVICE), null); } else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) { logger.info(">>> Found fragment route: " + tm.toString() + " <<<"); routeMete = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null); } else { throw new RuntimeException("ARouter::Compiler >>> Found unsupported class type, type = [" + types.toString() + "]."); } categories(routeMete); // if (StringUtils.isEmpty(moduleName)) { // Hasn't generate the module name. // moduleName = ModuleUtils.generateModuleName(element, logger); // } }
因我们的目的主要是分析跨模块API调用,这里只需要关注
if (types.isSubtype(tm,iProvider)) {// IProvider
logger.info(">>> Found provider route: " + tm.toString() +" <<<");
routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
}
发现是IProvider子类型后,将Route和element相关信息保存到RouteMeta结构体中。
/** * It contains basic route information. * * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a> * @version 1.0 * @since 16/8/24 09:45 */ public class RouteMeta { private RouteType type; // Type of route private Element rawType; // Raw type of route private Class<?> destination; // Destination private String path; // Path of route private String group; // Group of route private int priority = -1; // The smaller the number, the higher the priority private int extra; // Extra data private Map<String, Integer> paramsType; // Param type public RouteMeta() { }
routeMete信息会在categorites里排序后,按照组信息被加到:
private Map<String, Set<RouteMeta>>groupMap =new HashMap<>();//
ModuleName and routeMeta.
第四段
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(providerParamSpec); // Start generate java source, structure is divided into upper and lower levels, used for demand initialization. for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) { String groupName = entry.getKey(); MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(groupParamSpec); // Build group method body Set<RouteMeta> groupData = entry.getValue(); for (RouteMeta routeMeta : groupData) { switch (routeMeta.getType()) { case PROVIDER: // Need cache provider's super class logger.info("print rawtype:"+routeMeta.getRawType()); List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces(); for (TypeMirror tm : interfaces) { if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself. // This interface extend the IProvider, so it can be used for mark provider logger.info(">>>sanme type"); loadIntoMethodOfProviderBuilder.addStatement( "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))", (routeMeta.getRawType()).toString(), routeMetaCn, routeTypeCn, ClassName.get((TypeElement) routeMeta.getRawType()), routeMeta.getPath(), routeMeta.getGroup()); } else if (types.isSubtype(tm, iProvider)) {
Start generate java source.
4.1 第一部分生成方法:
public void loadInto(Map<String, RouteMeta> providers) {}
4.2 第二部分遍历groupMap:
先生成方法:
public void loadInto(Map<String, RouteMeta> atlas) {
}
然后遍历单个组内的RouteMeta信息:
for (RouteMeta routeMeta : groupData) {
switch (routeMeta.getType()) {
case PROVIDER:
// handle provider case.
}
}
重点关注如何处理Provider case.
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
这里是拿到被Route注解的类实现了哪些接口 。
for (TypeMirror tm : interfaces) {
if (types.isSameType(tm, iProvider)) {
..................
}else if(types.isSubtype(tm,iProvider)){
..................
}
}
遍历interfaces,判断interface是自己实现了IProvider还是interface继承了IProvider.为什么要加两个判断?没有看太明白.
loadIntoMethodOfProviderBuilder.addStatement( "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))", (routeMeta.getRawType()).toString(), routeMetaCn, routeTypeCn, ClassName.get((TypeElement) routeMeta.getRawType()), routeMeta.getPath(), routeMeta.getGroup());
在ARouter\$\$Providers\$\$app类loadInfo方法中添加:
providers.put("com.router.service.IService", RouteMeta.build(RouteType.PROVIDER,
MyService.class,
"/service/hello",
"service", null, -1, -2147483648));
loadIntoMethodOfGroupBuilder.addStatement( "atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))", routeMeta.getPath(), routeMetaCn, routeTypeCn, ClassName.get((TypeElement) routeMeta.getRawType()), routeMeta.getPath().toLowerCase(), routeMeta.getGroup().toLowerCase());
在ARouter\$\$Group\$\$service类loadInfo方法中添加:
atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, MyService.class,"/service/hello","service",
null, -1, -2147483648));
4.3 第三部分生成Group java source file
String groupFileName = NAME_OF_GROUP + groupName; JavaFile.builder(PACKAGE_OF_GENERATE_FILE, TypeSpec.classBuilder(groupFileName) .addJavadoc(WARNING_TIPS) .addSuperinterface(ClassName.get(type_IRouteGroup)) .addModifiers(PUBLIC) .addMethod(loadIntoMethodOfGroupBuilder.build()) .build() ).build().writeTo(mFiler);
将4.1和4.2生成的方法,写入以groupFileName命名的文件中。最终生成的类如下:
package com.alibaba.android.arouter.routes; import com.alibaba.android.arouter.facade.enums.RouteType; import com.alibaba.android.arouter.facade.model.RouteMeta; import com.alibaba.android.arouter.facade.template.IRouteGroup; import com.router.service.MyService; import java.lang.Override; import java.lang.String; import java.util.Map; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter\$\$Group\$\$service implements IRouteGroup { @Override public void loadInto(Map<String, RouteMeta> atlas) { a654 atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, MyService.class, "/service/hello", "service", null, -1, -2147483648)); } }
第五段
// Wirte provider into disk String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName; JavaFile.builder(PACKAGE_OF_GENERATE_FILE, TypeSpec.classBuilder(providerMapFileName) .addJavadoc(WARNING_TIPS) .addSuperinterface(ClassName.get(type_IProviderGroup)) .addModifiers(PUBLIC) .addMethod(loadIntoMethodOfProviderBuilder.build()) .build() ).build().writeTo(mFiler); logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<"); // Write root meta into disk. String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName; JavaFile.builder(PACKAGE_OF_GENERATE_FILE, TypeSpec.classBuilder(rootFileName) .addJavadoc(WARNING_TIPS) .addSuperinterface(ClassName.get(elements.getTypeElement(ITROUTE_ROOT))) .addModifiers(PUBLIC) .addMethod(loadIntoMethodOfRootBuilder.build()) .build() ).build().writeTo(mFiler);
最后生成两个类:
package com.alibaba.android.arouter.routes; import com.alibaba.android.arouter.facade.enums.RouteType; import com.alibaba.android.arouter.facade.model.RouteMeta; import com.alibaba.android.arouter.facade.template.IProviderGroup; import com.router.service.MyService; import java.lang.Override; import java.lang.String; import java.util.Map; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter\$\$Providers\$\$app implements IProviderGroup { @Override public void loadInto(Map<String, RouteMeta> providers) { providers.put("com.router.service.IService", RouteMeta.build(RouteType.PROVIDER, MyService.class, "/service/hello", "service", null, -1, -2147483648)); } }
和
package com.alibaba.android.arouter.routes; import com.alibaba.android.arouter.facade.template.IRouteGroup; import com.alibaba.android.arouter.facade.template.IRouteRoot; import java.lang.Class; import java.lang.Override; import java.lang.String; import java.util.Map; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter\$\$Root\$\$app implements IRouteRoot { @Override public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) { routes.put("service", ARouter$$Group$$service.class); } }
注解处理器处理Route分析完毕。
一个Route注解,经过自定义RouteProcessor的处理,最终生成三个类。那这些类有什么用呢???接下来分析arouter-api是如何做到块模块调用到暴露的服务。
相关文章推荐
- ARouter 学习- 源码分析如何做到控制反转
- dubbo源码分析12——服务暴露3_doExportUrls()方法分析
- dubbo源码学习(五)dubbo暴露服务的过程
- 7、dubbo源码分析 之 服务本地暴露
- Dubbo暴露服务和引用服务的实现源码分析
- 8、dubbo源码分析 之 服务远程暴露(上)
- dubbo源码分析(一) 服务暴露--服务引用
- Dubbo源码学习--服务是如何引用的
- Dubbo源码分析----暴露服务
- Openstack学习笔记之——Neutron-server服务加载与启动源码分析(三)
- dubbo服务暴露过程源码分析
- 1.如何分析学习源码
- Dubbo源码分析之服务暴露
- Dubbo源码学习--服务是如何发布的
- Dubbo 源码学习(五)dubbo暴露服务的过程
- 6、dubbo源码分析 之 服务暴露概述
- Dubbo2.7源码分析-如何发布服务
- Android 框架学习2:源码分析 EventBus 3.0 如何实现事件总线
- dubbo源码分析10——服务暴露1_export()方法分析
- [置顶] Dubbo暴露服务和引用服务的实现源码分析