- hasNextService() 方法解析出了 SPI 实现类的的全限定名 nextName,通过反射,获取 SPI 实现类的类定义 Class 。
- 然后,尝试通过 Class 的 newInstance 方法实例化一个 SPI 服务对象 。如果成功,则将这个对象加入到缓存 providers 中并返回该对象 。
private boolean hasNextService() {if (nextName != null) {return true;}if (configs == null) {try {// 1.拼接 META-INF/services/ + SPI 接口全限定名// 2.通过类加载器,尝试加载资源文件// 3.解析资源文件中的内容String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);} catch (IOException x) {fail(service, "Error locating configuration files", x);}}while ((pending == null) || !pending.hasNext()) {if (!configs.hasMoreElements()) {return false;}pending = parse(service, configs.nextElement());}nextName = pending.next();return true;}private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn = nextName;nextName = null;Class<?> c = null;try {c = Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn+ " not a s");}try {S p = service.cast(c.newInstance());providers.put(cn, p);return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error();// This cannot happen}3.3 SPI 和类加载器通过上面两个章节中,走读 ServiceLoader 代码,我们已经大致了解 Java SPI 的工作原理,即通过 ClassLoader 加载 SPI 配置文件,解析 SPI 服务,然后通过反射,实例化 SPI 服务实例 。我们不妨思考一下,为什么加载 SPI 服务时,需要指定类加载器 ClassLoader 呢?
学习过 JVM 的读者,想必都了解过类加载器的双亲委派模型(Parents Delegation Model) 。双亲委派模型要求除了顶层的 BootstrapClassLoader 外,其余的类加载器都应有自己的父类加载器 。这里类加载器之间的父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)的关系实现 。
双亲委派机制约定了:一个类加载器首先将类加载请求传送到父类加载器,只有当父类加载器无法完成类加载请求时才尝试加载 。
双亲委派的好处:使得 Java 类伴随着它的类加载器,天然具备一种带有优先级的层次关系,从而使得类加载得到统一,不会出现重复加载的问题:
- 系统类防止内存中出现多份同样的字节码
- 保证 Java 程序安全稳定运行
例如:java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 的类并放到 classpath 中,程序可以编译通过 。因为双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 classpath 中的 Object 优先级更高,因为 rt.jar 中的 Object 使用的是启动类加载器,而 classpath 中的 Object 使用的是应用程序类加载器 。正因为 rt.jar 中的 Object 优先级更高,因为程序中所有的 Object 都是这个 Object 。
双亲委派的限制:子类加载器可以使用父类加载器已经加载的类,而父类加载器无法使用子类加载器已经加载的 。——这就导致了双亲委派模型并不能解决所有的类加载器问题 。Java SPI 就面临着这样的问题:
- SPI 的接口是 Java 核心库的一部分,是由 BootstrapClassLoader 加载的;
- 而 SPI 实现的 Java 类一般是由 AppClassLoader 来加载的 。BootstrapClassLoader 是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库 。它也不能代理给 AppClassLoader,因为它是最顶层的类加载器 。这也解释了本节开始的问题——为什么加载 SPI 服务时,需要指定类加载器 ClassLoader 呢?因为如果不指定 ClassLoader,则无法获取 SPI 服务 。
经验总结扩展阅读
-
活了一把年纪了|婚礼上,我遇到了妻子以前的恩客,才知妻子之前的经历我该原谅吗
-
何药师健康科普|长春胺和甲钴胺有什么区别?保护脑细胞和神经哪个更好?药师告诉您
-
拉玛十世|情断慕尼黑,拉玛十世已逾半年未回德国有原因,他并非要改过自新
-
属兔人2022年9月健康运势怎么样 属兔人2022年9月运势完整版
-
-
每个人心里都有一个关于爱情的梦 频率相同的人,才是往后路上的最佳伴侣
-
美容 人人都能驾驭的5款短发造型,随手一拨都是气质感
-
-
女神“不老女神”林志玲,46岁依然肤白貌美,她的养生秘籍终于公开了
-
-
-
-
-
发票自助领用机怎么用,自助发票机必须本人去买发票吗
-
秦始皇|霍金去世前曾称“秦始皇还活着,只是走不出地宫”,这事是真的?
-
-
-
-
-
世界首个唾液孕检试剂盒即将上市 唾液孕检试剂盒怎么使用准确率高吗