spi:动态服务加载
github:spi学习demo
掘金:spi机制
学习了解注解和apt代码注入的意外之喜,曾经在项目中也看到过对应的文件夹及其开发模式,只是当时不知道也不了解,碰到了肯定要盘它,和先前文章一样,spi主要是学习总结所以更多的是参考上面的文章及其文章中对应的代码工程demo总结自己对spi的理解,首先表示对文章巨人的感谢,阅读文章者也建议先阅读上面文章再对下面文章比对,有不准确的欢迎指正讨论。
- 学习spi需要掌握的点:
- 如何实现spi机制
- spi机制优缺点
- spi的使用场景
- 浅谈对spi机制的理解
- spi的实现原理
- 如何实现spi机制:
- 声明接口(对功能的抽象)
-
在各个module中对接口进行实现并声明service provider文件声明文件有下面需要注意的点:
* 文件的文件夹路径如图(spi实现的核心类serviceload会根据这个路径结构读取对应的声明文件获取到具体的实现类)
service provider路径
* 声明文件的文件名字必须和先前声明的接口类的全类名(包含包名+类名)保持一致,原因和文件夹路径一样。
* 服务文件的注册声明支持多实现类的声明,换行声明即可。 - 在主module或者单独的spi module中提供utils读取各个module的文件及其对应的实现类并反射实现其对象缓存到map中方便后续使用。具体代码如下:
/**
* 初始化各个moduel的Application
* @param application
*/
public void initAllModuelApplication(@NonNull Application application) {
ServiceLoader<AppInit> loader = ServiceLoader.load(AppInit.class);
Iterator<AppInit> mIterator = loader.iterator();
while (mIterator.hasNext()) {
mIterator.next().applicationInit(application);
}
}
- spi机制的优缺点:
- 优点:
* module解耦,通过spi在跨module中调用中不需要硬编码对应的类对象,提供服务的module提供自己的声明文件即可 - 缺点:
* serviceload读取声明文件获取对应的实现类并反射其对象,此处使用反射多少对于性能有影响,好在在类中做了缓存,不需要每次调用都去反射。
* 单对单跨module调用还好,对于多module开发中,serviceload不能具体区分单个module,批量通用调用。
* spi机制的基础是找到声明文件并通过文件的声明类找到具体的实现类所以当前module必须引入工程中,不然不会找到当前module的实现而且不会报错,对于module的引入比较考究。
- spi的使用场景:
- 多module需要init等通用逻辑,使用spi机制可以很方便的实现
- 跨module需要api调用通信(根据场景要考虑其反射的性能影响)
- spi提供注解处理器的具体实现,借助于javac工具实现编译器代码生成,apt借助其实现。
- spi的理解:
spi的实现主要是解耦多module开发,先前在module中使用另一个module的对象,需要在当前module中new一个对象硬编码的方式调用,借用spi机制则不再需要直接new对象硬编码调用,服务module按照约定对外提供服务文件并指向服务的实现类,借助serviceload的反射调用对象实现module之间的解耦。spi只是module解耦中的一种,还有其他的多模块之间的解耦方式。 - spi的实现原理只要借助于类serviceload,代码量不大上面的文章也都有此处不再详述。核心要素如下:
- serviceload持有当前线程的类加载器(也可以指定),结合给定的接口类和默认文件夹地址扫描整个编译期的对应接口文件并获取到对应的接口实现类并反射实现对应的对象。
- serviceload中持有一个LazyIterator(懒加载迭代器)load方法加载对应的接口类的时候会先从LazyIterator容器中获取,没有才会真正的去执行上面的逻辑生成并存储到对应的LazyIterator中
网友评论