美文网首页
七、SPI使用及源码分析

七、SPI使用及源码分析

作者: 社会yang | 来源:发表于2019-12-04 11:21 被阅读0次

SPI案列

在pom文件中引入mysql的相关依赖

此处说明:SPI加载到了 com.mysql.jdbc.Driver与com.mysql.fabric.jdbc.FabricMySQLDriver,并且其类加载器都是为AppClassLoader,当前线程上文的类加载器也为AppClassLoader,ServiceProver的类加载器为null说明是启动类加载器,

那么启动类加载器如何加载到 AppClassLoader所管理的目录下的mysql相关的class文件的呢?

SPI文档说明

A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file's name is the fully-qualified binary name of the service's type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line. Space and tab characters surrounding each name, as well as blank lines, are ignored. The comment character is '#' ('\u0023', NUMBER SIGN); on each line all characters following the first comment character are ignored. The file must be encoded in UTF-8.

If a particular concrete provider class is named in more than one configuration file, or is named in the same configuration file more than once, then the duplicates are ignored. The configuration file naming a particular provider need not be in the same jar file or other distribution unit as the provider itself. The provider must be accessible from the same class loader that was initially queried to locate the configuration file; note that this is not necessarily the class loader from which the file was actually loaded.

Providers are located and instantiated lazily, that is, on demand. A service loader maintains a cache of the providers that have been loaded so far. Each invocation of the iterator method returns an iterator that first yields all of the elements of the cache, in instantiation order, and then lazily locates and instantiates any remaining providers, adding each one to the cache in turn. The cache can be cleared via the reload method.

从上说的描述中可以发现几个关键点:

1:自定义SPI必须有一个 META-INF/services目录

2:在META-INF/services下会有一个类或接口名称文件,里面的内容是由加载类的类全名

3:如果在文件中出现多个相同的配置会忽略相同的副本

4:类的加载是懒加载的

我们可以看到上述SPI案列的确存在一个META-INF.services文件夹,里面的文件问Driver接口类全路径

里面的内容就是为最终加载的类也就是文档中的Provider

SPI源码分析

从ServiceLoader.load处开始分析

此处的重点:ClassLoader cl = Thread.currentThread().getContextClassLoader();

此处保存了: service 为所要加载的服务接口或者类

                    loader    线程上下文的类加载,如不存在会将系统类加载赋值

此处的 providers    是加载类的map集合是相当于内存缓存的意思

        lookupIterator    实列化了一个懒加载的集合,进入实列化代码查看

将service与loader保存为类的成员变量

进入serviceLoader.iterator(),实际上是执行的ServiceLoader.iterator()方法

反回了一个Iterator对象

继续向下执行,iterator.hasNext()实际上就是返回的Iterator对象的hasNext方法

此时 knownProviders 不成立,会执行 lookupIterator.hasNext(),lookupIterator实际上是上述定义的懒加载LazyIterator,执行器hasNext方法

最终都会执行hasNextService()方法

最终会在 META-INF/services/下services.getName文件中找实现类,如果能找到就返回true,并且保存nextName为找到的Provider

继续执行    iterator.next()

最终都会之心LazyIterator的next方法

最终都会执行 nextService方法

此处再次判断是否存在Provider,根据nextName实列化对象,最终将对象保存到 providers中,providers的key为类的名称value为实列化的对象,最终将对象返回,至此已经完成SPI的加载过程。

相关文章

网友评论

      本文标题:七、SPI使用及源码分析

      本文链接:https://www.haomeiwen.com/subject/bpvkgctx.html