共计 3182 个字符,预计需要花费 8 分钟才能阅读完成。
零碎整顿下 Java SPI,Dubbo SPI,Spring SPI。
Java SPI
简述
有点相似于策略设计模式,定义好接口,在文件中写实现类的全路径名。调用 ServiceLoader.load
的时候返回一个迭代器,他外部是一个懒加载,当调用 hasNext
的时候才会依据全路径名读取文件,调用 next
的时候才会实例化。
实质上就是,获取接口全路径名,安标准去该门路下按行读取文件,而后用同一个类加载器加载类,返回。(源码很简略,就不多说了,应用办法看图)
长处
相似于模板办法,定义好接口,实现能够齐全由第三方来写,依照标准就行。
毛病
人家本人也说了,一个 简略 的服务提供者的加载工具。人家源码开发者就没想写的多好用,就是写个 demo 秀操作呢。
Dubbo SPI
简介
官网:https://dubbo.apache.org/zh/d…
长处
- JDK 规范的 SPI 会一次性实例化扩大点所有实现,如果有扩大实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩大点加载失败,连扩大点的名称都拿不到了。比方:JDK 规范的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败起因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不反对 ruby,而不是真正失败的起因。
-
减少了对扩大点 IoC 和 AOP 的反对,一个扩大点能够间接 setter 注入其它扩大点。
然而我跟了源码,不是一次性加载所有,是调用的时候创立(java 版本 1.8.0_211)剖析
- 既然是增强,那其实实质上也是差不多的,就是获取到自定义实现类。我先看了下目录下的文件,是 key,value 的模式,那么猜想就是依据增强点应该就是依据 key 获取到指定的实现类。看了下源码,调用
com.alibaba.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
的时候,就会进行解析对应的文件,后果如下
- adaptive
然而外面没有自适应的,比方ExtensionFactory
获取的时候,解析到adaptive
标签,会缓存到cachedAdaptiveClass
如果没有获取到他会拼接字符串 + 编译的形式生成一个,代码如下
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
}
而后就是依据 url 中的协定,去找到对应实现,对应的这一行
(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
前面就没啥好说的了,调用对应的 refer。
Spring SPI
上一篇剖析了,这里就不多赘述了,整体思路差不多,框架定义接口,第三方 jar 包自定义实现,依照标准在指定地位定义 key,value
正文完