[传送门] Java SPI,请参考:Java SPI机制解读 && 源码解读,建议:先看完Java SPI后,再来了解Dubbo SPI ^_^
[传送门] JDK动态代理,请参考:JDK动态代理 如需了解JDK动态代理和Cglib动态代理区别,附:Cglib动态代理
1.什么是Dubbo SPI
Dubbo SPI,是对Java SPI机制的扩展,与Java SPI机制类似,原理相同,都是一种动态服务发现机制,来实现相关的操作。Dubbo框架就是通过 Dubbo SPI 机制,从而实现 Dubbo 自定义协议、Dubbo 自定义路由......等很多模块的自定义。如想了解决SPI机制的原理实现原理,请点击本文开篇传送门,来做进一步了解,然后在回来瞅这篇
2.Dubbo SPI与Java SPI的不同
Dubbo SPI
①配置文件存放路径不同:META-INF/dubbo/、META-INF/dubbo/internal/、META-INF/services/(3种)
②配置文件中内容不同:KV键值对形式(key=value形式)
③增加了Spring IOC 和 AOP 的支持
Java SPI
①配置文件存放路径不同:META-INF/services/(1种)
②配置文件中内容不同:接口实现类的全限定名
3.Dubbo SPI的扩展
由于文件中内容的不同,也就直接导致了Dubbo中无法直接使用JDK中提供的ServiceLoader类,来动态装载实现模块 。与之对应,Dubbo中提供了ExtensionLoader类。ExtensionLoader是扩展点载入器,用于载入Dubbo中的各种可配置组件,比如:动态代理方式(ProxyFactory)、负载均衡策略(LoadBalance)、RCP协议(Protocol)、拦截器(Filter)、容器类型(Container)、集群方式(Cluster)和注册中心类型(RegistryFactory)等。
Dubbo为了应对各种场景,它的所有内部组件都是通过这种SPI的方式来管理的,这也是为什么Dubbo需要将服务提供者配置文件设计成KV键值对形式,这个K就是我们在Dubbo配置文件或注解中用到的K,Dubbo直接通过服务接口(上面提到的ProxyFactory、LoadBalance、Protocol、Filter等)和配置的K从ExtensionLoader拿到服务提供的实现类。
同时,由于Dubbo使用了URL总线的设计,即很多参数通过URL对象来传递,在实际中,具体要用到哪个值,可以通过URL中的参数值来指定。(在下面源码分析中,你便会看到URL总线的体现)
4.Dubbo SPI实例
在dubbo中,支持多种协议。如:默认的dubbo协议、hessian协议、redis协议、webservice协议、rmi协议等等。但是如果我想用我自己编写的协议,那么这种扩展就得用Dubbo的SPI机制来实现了。
Dubbo框架相当于官方,它为我们提供了协议扩展的接口Protocol,Dubbo官方也为我们提供了上面协议的具体实现。如果我们需要使用自己的协议来发布服务,那么我们便需要实现该接口,重写接口里面的方法,即可实现用我们自己的协议来发布服务了
①创建一个MyProtocol类,实现Protocol接口,重写该接口的方法
package com.test.dubbo;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.RpcException;
/**
* 自定义的协议
* 每个接口重写方法是什么意思,参见 5.Dubbo SPI 源码解析,有详细解析
* 重写Protocol接口的方法(本例仅重写了一个getDefaultPort方法)
*/
public class MyProtocol implements Protocol {
@Override
public int getDefaultPort() {
return 8789;
}
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return null;
}
@Override
public <T> Invoker<T> refer(Class<T> aClass, URL url) throws RpcException {
return null;
}
@Override
public void destroy() {
}
}
②约定实现。
在resources目录下,新建META-INF/dubbo(或者META-INF/dubbo/internal、META-INF/services)目录。然后新建一个以"接口全限定名"为文件名的文件,本例为com.alibaba.dubbo.rpc.Protocol;文件内容为KV结构(K为自定义,V为实现类的全限定名,本例为com.test.dubbo.MyProtocol)
③使用 ExtensionLoader 类来加载配置文件中指定的实现
package com.test.dubbo;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Protocol;
import java.io.IOException;
public class testHello {
public static void main(String[] args) throws IOException {
//通过getExtension(String name),可以获得自定义的指定名称为key的协议
Protocol myProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myProtocol");
System.out.println(myProtocol.getDefaultPort());//输出:8789
//通过getDefaultExtension(),可以获得Dubbo框架默认的协议
Protocol defaultProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension();
System.out.println(defaultProtocol.getDefaultPort());//输出:20880
}
}
通过如上,我们便能够加载自己定义的协议了。从上面,我们也能够知道Dubbo框架默认协议为dubbo协议,端口为20880
Dubbo SPI机制介绍 && Dubbo SPI实例介绍,到此为止
如果本文对你有所帮助,那就给我点个赞呗 ^_^
End