1、SPI
在dubbo中包含了很多组件,各种组件又有不同的协议使得存在多种实现,而且在开发中都是推荐针对接口编程,那就存在一个问题了,如何选择合适的实现类呢?解决方案就是SPI
SPI的全名为Service Provider Interface,提供类似于插拔式的实现类选择能力。在java中使用的类是java.util.ServiceLoader
,然后在META-INF/services/
创建的具体文件中写上具体的实现类的类名称,就可以实现具体的类,在早期的JDBC中,必须得通过Class.forName("xxx")这种硬编码的方式去生成对应类的实例,但是现在可以直接通过SPI达到同样的目的
1.1、Java SPI DEMO
public interface Search {
void print();
}
public class FileSearch implements Search {
@Override
public void print() {
System.out.println("==== A ====");
}
}
public class SpiTest {
public static void main(String[] args){
ServiceLoader<Search> serviceLoader = ServiceLoader.load(Search.class);
Iterator<Search> it = serviceLoader.iterator();
while (it.hasNext()){
Search printImpl = it.next();
printImpl.print();
}
}
}
spi.Search
spi.FileSearch
spi.WebSearch
![](https://img.haomeiwen.com/i2064197/706af05b5bc8a9a2.jpg)
如上图,需要在资源文件的根目录创建一个META-INF.services的文件夹,新建一个Search接口完整路径的文件,本例子是spi.Search,里面填入实现类的类全程。如下图,确实调用了两个具体的实例,迭代分别执行。
![](https://img.haomeiwen.com/i2064197/f08e6c5faf441967.jpg)
1.2、Java SPI 源码实现
如下图,首先获取到fullName数据,然后调用getResource获取该文件内的资源
![](https://img.haomeiwen.com/i2064197/4ca7d09c44e426f8.jpg)
private Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}
然后解析文件内的内容,存入到一个ArrayList中,并返回其迭代器(后面肯定还差一步就是实例化)
![](https://img.haomeiwen.com/i2064197/5bec467a0de5524c.jpg)
很明显了,在获取到类信息,直接调用newInstance方法完成实例化操作
同时根据newInstance也可以知道对外提供的spi的实现类不能有自定义的构造函数
2、Dubbo SPI 配置
dubbo的spi和java自带的spi稍有区别,如上述的demo中,是在文件中写入什么实现类,就会去实现,如果需要获取其中的一个,则需要循环迭代获取处理,而在dubbo中则采用了类似kv对的样式,在具体使用的时候则通过相关想法即可获取,而且获取的文件路径也不一致
ExtensionLoader 类文件
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
如上述代码片段可知,dubbo是支持从META-INF/dubbo/
,META-INF/dubbo/internal/
以及META-INF/services/
三个文件夹的路径去获取spi配置
![](https://img.haomeiwen.com/i2064197/8840e3a34cb8c95a.jpg)
例如com.alibaba.dubbo.rpc.Protocol 文件内容
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=memcom.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
不过观察上述文件会发现,HttpProtocol是没有对应的k值,那就是说无法通过kv对获取到其协议实现类
后面通过源码可以发现,如果没有对应的name的时候,dubbo会通过findAnnotationName方法获取一个可用的name
网友评论