Service Provider Interface 介绍
在java6中引入了服务发现和加载的接口:SPI
SPI的使用
创建相关类
增加文件配置
在Resources目录下create建META-INF/services目录,在目录中创建文件,文件名称为:com.example.demo.MyService
文件中配置我们需要提供服务的类的全路径名称(包名+类名)
加载服务
输出结果:
Hello world!
Welcome!
SPI实现原理
ServiceLoader类介绍
从示例中,我们可以看到,SPI的实现主要在ServiceLoader类中。
ServiceLoader实现Iterable接口,所以我们可以直接使用foreach的方式循环获取加载的实现类:
ServiceLoader有三种加载方法可供使用
在load静态方法中,实现最终的ServiceLoader类创建,指定我们的Service和所需要使用的类加载器。
在ServiceLoader类的实例化构造函数执行如下:
最终在reload方法实现缓存清理和Iterator查找类:
其中,LazyIterator是SPI实现的核心
LazyIterator
Lazyiterator用于锁定资源文件,并负责加载文件资源、按行读取解析、实例化对应对象,并且LazyIterator提供了延迟加载,在ServiceLoader创建完毕后,并不会发生资源文件的读取和使用,仅在查找和使用时,才发生资源的初始读取。
加载资源文件
在这里,也定义了我们在使用ServiceLoader的配置规则:
加载时根据需要加载的类名称和PREFIX目录前缀确定文件并加载:
读取和验证
读取时,按行进行解析,需要确保每行字符内容是符合java类标准的定义形式:
规则:
1、每行的类名称中不能包含空字符和水平制表符\t
2、首字符符合java标识符定义
3、对剩余的字符判定是否符合java标识符(首字符以外的规范)
4、同一命名空间下的类仅创建一个,不过对于 !names.contains(ln)这里的判定,如果类实现过多,contains查询会存在性能问题(contains方法时间复杂度O(n),极端情况下值得优化)
实例化对象
对象在真正使用的时候,才进行创建
如果仅做next查询,是不会进行类实例化的。
类实例化,需要调用iterator.next()真正获取对象时发生
以上最终实现服务类的实现
在ServiceLoader的实现中,由于存在providers存在,因此当providers中存在数据时,在使用ServiceLoader查找使用类时,将首先遍历providers中的实现。全部遍历后,继续遍历configs配置中未加载的部分,进行加载,最终将所有使用的类,一点点载入
版权声明: 本文为 InfoQ 作者【Skysper】的原创文章。
原文链接:【http://xie.infoq.cn/article/04f24f1ed4c43f11ca2e2a07e】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论