前言
如果觉得博主很帅或者这篇文章能帮助你的话,请不要吝啬你的点赞和收藏~这会对我的创作动力加满油,继续努力写更多能帮助到大家的文章。
在上一篇 ARoute解析(前)- 注解、注解处理器、JavaPoet 中为这章做了一些技术铺垫,如果对注解、注解处理器和JavaPoet不太熟悉的小伙伴可以先移步看上一篇文章了解一下相关技术。
接下来我们会了解以下两部分:
- ARouter编译时到底做了什么?产出了什么?
- ARouter是如何解析注解并且生成对应的类?
1.ARouter编译时到底做了什么?
在讲ARouter的编译期原理时候,我们要先知道ARouter编译期所做的事情到底是什么?有什么意义?这样我们才能继续保持好奇去挖掘其原理。
这里不绕圈子,直接上总结:
ARouter在编译期中,提前将声明了Router等注解的类或参数提前进行解析,这么做的好处就是在运行时不需要再额外的耗时解析Router注解,提高了ARouter的运行效率!
提炼成一句话就是在==编译期进行预处理,准备好数据!==
那么我们挖掘原理的方向无非就是去了解:
- 怎么进行预处理?
- 怎么准备好数据?
另外了解了这种思路之后,我们也可以思考自己的项目中有没事情或数据可以在编译期就提前做好,针对这方面对项目进行优化。
ok,废话不多说,兄弟萌开学!
1.1 ARouter编译时产出物一览
在讲原理之前,我还是得先了解清楚一下ARouter编译器预处理的产出物,我们知道ARouter的核心相关有以下几个:
- 路由相关
- Activity
- Service
- Fragment
- AutoWired(参数)
- Interceptor(路由跳转的拦截器)
- IProvider(模块暴露的服务)
- Group(路由组)
当我们在上面相关类(除了路由组和AutoWired)上声明@Route注解后,ARouter在编译期就会扫描项目中有关@Route的注解的类,将他们分好组保存好。接下来看看实际产出的成果:
我们可以看出ARoute在编译期生成出以下文件:
- 路由组相关的文件
- 服务文件
- 拦截器文件
- Root文件
- Autowired文件
1.1.1 路由组文件介绍
首先说一下,每个路由组都会独立生成一个Java文件,文件名格式为ARouter{groupName}。
比如我声明了两个Activity,分别是 @Route(path="/main/MainAcitivy")与@Route(path="/sec/SecAcitivy"),路径中的第一部分为路由组,所以ARouter就将他分成了两个main和sec的组文件。
接下来我们看看Main路由组下的类是长什么样子的
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER.
*/
public class ARouter$$Group$$main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", null, -1, -2147483648));
atlas.put("/main/mainFragment", RouteMeta.build(RouteType.FRAGMENT, MainFragment.class, "/main/mainfragment", "main", null, -1, -2147483648));
atlas.put("/main/mainServiceImpl", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainserviceimpl", "main", null, -1, -2147483648));
}
}
我们可以从一开始的注释看出来,该类是由ARouter生成的,该类做了以下几件事:
- 实现了IRouteGroup接口,重写了loadInto方法。
- 将该组下的路由相关元素信息保存到Map中。
- Activity元素
- Fragment元素
- Provider元素
尤其可见路由组文件存在的意义就是保存该组下的元素。
1.1.2 Providers文件
/**
* 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.example.demo.SecService", RouteMeta.build(RouteType.PROVIDER, SecServiceImpl.class, "/sec/secServiceImpl", "sec", null, -1, -2147483648));
providers.put("com.example.demo.MainService", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainServiceImpl", "main", null, -1, -2147483648));
}
}
Providers与Group不一样的是,该类继承于IProviderGroup并且Providers文件只有生成一个,会将项目中的Provider统一️保存到这里。
1.1.3 Root文件
/**
* 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("main", ARouter$$Group$$main.class);
routes.put("sec", ARouter$$Group$$sec.class);
}
}
Root文件也是只会生成一个,它会将我们的每个组对应的类保存到一个总的routesMap中
1.1.4 Autowired
@Autowired的作用是自动生成解析Activity或Fragment中的参数的一个类,我们通过示例来了解一下是如何声明并且最后接续生成出来时怎么样的。
首先我们声明一个Activity,声明一些页面参数并且标记Autowired注解。
@Route(path = "/main/mainActivity")
class MainActivity : ComponentActivity() {
@JvmField
@Autowired //为该参数
var spec: String? = null
@JvmField
@Autowired
var testObj: TestObj? = null
}
class TestObj() : Parcelable {
//...实现Parcelable接口函数(这里忽略)
}
接着我们看看编译期,ARouter会生成出怎么样的解析类
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class MainActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
MainActivity substitute = (MainActivity)target;
substitute.spec = substitute.getIntent().getExtras() == null ? substitute.spec : substitute.getIntent().getExtras().getString("spec", substitute.spec);
substitute.testObj = substitute.getIntent().getParcelableExtra("testObj");
}
}
ARouter生成出一个格式为{className}Autowired的解析类,继承自ISyringe接口,并且实现inject方法。
在方法中通过强转target为目标类,声明了Autowired注解的参数,会通过getIntent()方法来获取。
最后我们只要在Activit中调用,就完成了参数自动解析。
ARouter.getInstance().inject(this);
1.1.5 Interceptors
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(2, MainIntercept.class);
interceptors.put(8, SecIntercept.class);
}
}
与Providers一样,Interceptors也只会全局生成一个文件,存放拦截器。
1.1.6 编译产出物总结
那看到这里,ARouter的编译期产出物基本上看了一遍了,我们可以通过生成的Group,Providers,Root文件得出,ARouter的预编译所做的事情其实就是提前将标注了@Route等注解的类或参数分好组进行保存,然后再由Root保存每个组的信息,服务则由Providers、拦截器则由Interceptors统一保存。
那接下来我们就要开始分析ARoute是如何做到:
- ARouter是如何扫描与解析标记了@Route的类?
- ARouter是如何扫描与解析标记了@Autowired的参数?
- ARouter是如何生成上面的文件的?
2. 编译期原理解析
我们通过上一章文章知道,在编译期处理注解需要通过注解处理器进行对注解的处理,而生成Java类通过JavaPoet来处理。
2.1 BaseProcessor
在开始讲各个注解处理器之前,我们先了解一下ARouter中注解处理器的基类,上源码
public abstract class BaseProcessor extends AbstractProcessor {
//文件工具类
Filer mFiler;
//编译期Log工具类
Logger logger;
//处理 Element 的工具类,用于获取程序的元素,例如包、类、方法。
Elements elementUtils;
//处理 TypeMirror 的工具类,用于取类信息
Types types;
TypeUtils typeUtils;
// 项目名称
String moduleName = null;
// 是否生成路由文档
boolean generateDoc;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//1.初始化工具类
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
typeUtils = new TypeUtils(types, elementUtils);
logger = new Logger(processingEnv.getMessager());
//2.读取我们在build中的配置
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
//获得ModuleName
moduleName = options.get(KEY_MODULE_NAME);
//是否生成路由描述文档
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
}
}
//设置支持读取的配置(分别是ModuleName与是否生成路由文档)
@Override
public Set<String> getSupportedOptions() {
return new HashSet<String>() {{
this.add(KEY_MODULE_NAME);
this.add(KEY_GENERATE_DOC_NAME);
}};
}
}
BaseProcessor的作用就是初始化各种工具类与配置,并没有做具体解析的事情,那么接着我们看具体的注解处理器都做了什么?
2.2 Route注解处理器
那么我们先关注最核心的Route注解处理器,它负责解析@Route注解与生成路由组与Root文件。
接下来我们通过源码+注释的方式来讲解,各位留意源码中的注释来理解原理。
2.2.1 RouteProcessor成员与初始化
首先我们先了解一下其成员与初始化的逻辑。
//自动注册到编译器中
@AutoService(Processor.class)
//支持解析@Route与@AutoWired注解
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
//存放着每个组的RouteMeta元素
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>();
//这里负责存放每个组生成出来的文件c
private Map<String, String> rootMap = new TreeMap<>();
//Provider的类型
private TypeMirror iProvider = null;
//负责写入路由描述文档
private Writer docWriter;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//检查是否需要生成路由描述文件,按需初始化Writer
if (generateDoc) {
try {
docWriter = mFiler.createResource(
StandardLocation.SOURCE_OUTPUT,
PACKAGE_OF_GENERATE_DOCS,
"arouter-map-of-" + moduleName + ".json"
).openWriter();
} catch (IOException e) {
logger.error("Create doc writer failed, because " + e.getMessage());
}
}
//初始化IPROVIDER类型对象
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
}
2.2.2 RouteProcessor解析与生成流程
接下来我会通过源码+注释的方式来一步步解释RouteProcessor的工作原理,但是代码量还是有点庞大的,所以我先总结一下RouteProcessor的工作内容,以便大家先有个大概的认知,RouteProcessor主要做得的是以下事情:
- 解析项目中全部注解了Route的类,组装成RouteMeta并且按照路由组分好类
- Provider与Interceptor也会统一额外的集中保存
- 通过JavaPoet生成相关类,这里会有大量JavaPoet组装代码
- 生成路由文档
虽然代码量比较大,但是无非是做以上四件事,那么接下来上源码!
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
//...省略初始化与成员变量
//1.当编译器开始编译时,会调用该方法
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
//获取有关Route注解的类元素
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;
}
//具体的解析方法
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
//提前准备好合法的类类型,我们可以看到ARouter支持Activity、Service、Fragment(包括V4包的)
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
//提前准备好JavaPoet需要的类属性等
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
//接下来准备JavaPoet的方法组装
/*
准备Root文件loadInto方法中的参数类型
void loadInto(Map<String, Class<? extends IRouteGroup>> routes) 中的 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))
)
);
/*
准备Group文件loadInto方法中的参数类型
void loadInto(Map<String, RouteMeta> atlas) 中的 Map<String, RouteMeta>
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
上面只是声明了参数类型,但是不是一个完整的参数,缺少了命名,下面就是补充命名组装成完整参数,分别是以下几个:
1.Map<String, Class<? extends IRouteGroup>> routes
2.Map<String, RouteMeta> atlas
3.Map<String, RouteMeta> providers
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*
这里是开始组装Root中的loadInto方法,方法体会在后面添加,目前生成如下内容
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
}
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
/*
接下来就是遍历项目中的每个注解了Route的元素
*/
for (Element element : routeElements) {
//获取该元素的类型对象
TypeMirror tm = element.asType();
//获取注解
Route route = element.getAnnotation(Route.class);
//声明一个Route的元信息对象
RouteMeta routeMeta;
//判断元素是否继承于Activity或Fragment
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
//该Map保存着该类下声明了Autowired注解的成员变量名称与其类型
Map<String, Integer> paramsType = new HashMap<>();
//该Map保存着该类下声明了Autowired注解的成员变量名称与Autowired注解对象
Map<String, Autowired> injectConfig = new HashMap<>();
//该方法就是遍历类与递归其父类,将声明了Autowired注解的类成员变量名、变量类型与其Autowired注解保存到以上两个Map中。
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
//如果是Activity,则声明一个Activity类型的RouteMeta元信息对象,并且保存其信息,刚刚解析的有关该类下的Autowired参数也会塞进去
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment同理Activity
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
//最后将injectConfigMap塞进去
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) {
//如果是实现了iProvider接口的,则创建一个PROVIDER类型的RouteMeta
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) {
//Service同理
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
//如果该元素不是以上支持的类型,则抛出异常,并且终止编译行为
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
//该方法名已经说明了,我们会开始按组分类,以1对多的关系保存到groupMap中,下面会分析该方法,但是目前先知道作用即可。
categories(routeMeta);
}
//到这里,基本Route注解的解析流程基本结束,下面就是生成Java类的流程了。
/*
生成Provider类中的方法Into方法,如下:
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
*/
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
//接下来开始遍历每个路由组之前保存好的各个RouteMate,开始生成路由组文件
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
//获取路由组名称
String groupName = entry.getKey();
/*
开始构建路由组文件中的into方法:如下
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
}
*/
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
// 刚刚我们生成了Route的loadInto方法,但是没有方法体,接下来就开始往该方法中添加方法体,(这里会顺便将我们之前生成的Provider中的loadInto方法体添加方法体。)
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
//首先会填充Provider中的into方法体(这里与路由组文件无关)
switch (routeMeta.getType()) {
case PROVIDER:
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces)
/*
遍历该类实现的接口,直接与间接实现IProvider接口的都是符合条件添加到方法体中,生成如下方法体并且添加到方法中:
providers.put("com.alibaba.android.arouter.facade.template.IInterceptor", RouteMeta.build(RouteType.PROVIDER, SecIntercept.class, "/sec/secIntercept", "sec", null, -1, -2147483648));
最后组成一个完整的方法,如下:
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.facade.template.IInterceptor", RouteMeta.build(RouteType.PROVIDER, SecIntercept.class, "/sec/secIntercept", "sec", null, -1, -2147483648));
}
*/
if (types.isSameType(tm, 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,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
/*
接下来就是将每个RouteMeta组装成方法体,并且设置进loadInto方法中,如下:
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", new java.util.HashMap<String, Integer>(){{put("testObj", 10); put("spec", 8); }}, -1, -2147483648));
最后方法体中就会多出该行,如下:
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", new java.util.HashMap<String, Integer>(){{put("testObj", 10); put("spec", 8); }}, -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,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
}
/*
遍历完路由组元素,并且都作为方法体加入到方法后,就会开始生成路由组完整类,然后输出到项目目录中,完整类如下:
public class ARouter$$Group$$main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", new java.util.HashMap<String, Integer>(){{put("testObj", 10); put("spec", 8); }}, -1, -2147483648));
atlas.put("/main/mainFragment", RouteMeta.build(RouteType.FRAGMENT, MainFragment.class, "/main/mainfragment", "main", null, -1, -2147483648));
atlas.put("/main/mainIntercept", RouteMeta.build(RouteType.PROVIDER, MainIntercept.class, "/main/mainintercept", "main", null, -1, -2147483648));
atlas.put("/main/mainServiceImpl", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainserviceimpl", "main", null, -1, -2147483648));
}
}
*/
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);
logger.info(">>> Generated group: " + groupName + "<<<");
//至此一个路由组的文件生成工作结束,将类名与文件名保存到rootMap中,方便下面生成Root文件
rootMap.put(groupName, groupFileName);
}
/*
总结一下上面流程中所做了的事情
1.生成每个路由组文件,并且将Activity、Service、Fragment、Provider这些元素转化成RouteMeta并且作为方法体,添加到其Into方法中的map参数中,最后生成一个类,生成内容示例如下:
public class ARouter$$Group$$main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", new java.util.HashMap<String, Integer>(){{put("testObj", 10); put("spec", 8); }}, -1, -2147483648));
atlas.put("/main/mainFragment", RouteMeta.build(RouteType.FRAGMENT, MainFragment.class, "/main/mainfragment", "main", null, -1, -2147483648));
atlas.put("/main/mainIntercept", RouteMeta.build(RouteType.PROVIDER, MainIntercept.class, "/main/mainintercept", "main", null, -1, -2147483648));
atlas.put("/main/mainServiceImpl", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainserviceimpl", "main", null, -1, -2147483648));
}
}
2.为Provider的loadInto方法体添加方法体,但是还未生成类,生成内容示例如下:
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.example.demo.SecService", RouteMeta.build(RouteType.PROVIDER, SecServiceImpl.class, "/sec/secServiceImpl", "sec", null, -1, -2147483648));
providers.put("com.example.demo.MainService", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainServiceImpl", "main", null, -1, -2147483648));
}
路由组的生成工作完成了,剩下就是Provider与Root文件的生成工作的收尾,我们接着看
*/
/*判断如果项目中生成过路由组,则往Root的loadInto方法中塞入方法体,示例如下:
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("main", ARouter$$Group$$main.class);
routes.put("sec", ARouter$$Group$$sec.class);
}
*/
if (MapUtils.isNotEmpty(rootMap)) {
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
//至此,Provider与Root的loadInto方法体已经补充完毕,开始生成类,并且输出成文件
/*
输出一个完整的Provider类,并且输出成文件,示例如下:
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.example.demo.SecService", RouteMeta.build(RouteType.PROVIDER, SecServiceImpl.class, "/sec/secServiceImpl", "sec", null, -1, -2147483648));
providers.put("com.example.demo.MainService", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainServiceImpl", "main", null, -1, -2147483648));
}
}
*/
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 + " <<<");
/*
输出一个完整的Root类,并且输出成文件,示例如下:
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("main", ARouter$$Group$$main.class);
routes.put("sec", ARouter$$Group$$sec.class);
}
}
*/
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}
}
当然我们剩下一些方法未解析,就是我们的分类方法,我们再简单看一下,看看ARoute是如何分类的?
private void categories(RouteMeta routeMete) {
//这里是校验路由名是否有问题与获得路由组名,就不展开说了
if (routeVerify(routeMete)) {
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
//从保存路由组的Map中获取该路由组的Set集合,如果没有则新建Set集合,如果有则直接将该RouteMeta保存到Set中
//然后再将路由名为Key,Set集合为Value保存到Map中,直到全部RouteMeta分类好
Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) {
Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
routeMetaSet.add(routeMete);
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else {
routeMetas.add(routeMete);
}
} else {
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
}
}
那么!RouteProcessor的源码就解析完毕,我们通过以上的分析知道了,RouteProcessor解析了Route注解,并且输出了以下三种文件
- 路由组文件
- Providers文件
- Root文件
那么最后就剩下InterceptorProcessor和AutowiredProcessor两个未分析,但是因为篇幅问题,而且我认为这两个Processor的思路是一致的,没必要重复去讲,在这里我就当布置个作业给大家~希望大家能带着思路去自己看一遍源码,加深这个ARouter编译期原理理解
最后做一个总结,我们一开始是带着两个问题的,分别是
- ARouter编译时到底做了什么?产出了什么?
- ARouter是如何解析注解并且生成对应的类?
我相信此刻为止,已经解开了疑问,剩下最后的篇章我会讲到ARoute运行期的原理,接下来我们会了解到
- 以上生成的文件,是什么时候用的?
- 路由的跳转、拦截器、暴露服务等等等原理是什么?
如果这篇文章对你有所帮助,希望你不要吝啬手中的赞和收藏,这对我来说十分重要,谢谢大家的收看!
我的其它系列文章: