最近看Dubbo源码的时候,最开始对Dubbo的自适应扩大始终没怎么看明确,参考其余的博客大多也就是把官网的代码解释搬过去,然并卵。
最初依照本人的了解来模仿了一下,心愿对大家能有所帮忙。
先定义接口类 SpiTest,有一个mySpi办法:
@SPI //标记为扩大接口public interface SpiTest{
void mySpi(URL url, String name);
}
有两个实现类S1和S2:
public class S1 implements SpiTest{
@Override
public void mySpi(URL url, String name) {
System.out.println("This is S1 : "+name);
}
}
public class S2 implements SpiTest{
@Override
public void mySpi(URL url,String name) {
System.out.println("This is S2 : "+name);
}
}
在META-INF/dubbo下创立文件com.xx.dubbospi.SpiTest
S1=com.zf.xx.dubbospi.S1
S2=com.z远程桌面f.xx.dubbospi.S2
在失常应用的时候能够通过 ExtensionLoader.getExtensionLoader(SpiTest.class).getExtension("S1")来获取SpiTest的某一个实现,然而如果在办法调用时不确定具体实现类怎么办?能够定义一个包装类SpiWrapper ,包装类不具体实现办法,只是依据参数获取对应的扩大对象来执行,依据传入的参数来获取到底时S1还是S2:
public class SpiWrapper implements SpiTest{
@Override
public void mySpi(URL url,String name) {
SpiTest spiTest = ExtensionLoader.getExtensionLoader(SpiTest.class).getExtension(url.getParameter("spi.test"));//通过参数指定须要加载的对象
spiTest.mySpi(url,name);
}
public static void main(String[] args) {
URL url = new URL("dubbo","123",999);//这里的URL是 org.apache.dubbo.common.URL
url = url.addParameter("spi.test","S2");
SpiWrapper spiWrapper = new SpiWrapper();
spiWrapper.mySpi(url,"tudou");
}
}
在SpiWrapper 中会依据url上的参数spi.test的值类决定到底取SpiTest的哪一个实现类,这样就实现的SPI的一个动静扩大。而在Dubbo中具体的应用须要先对SpiTest进行革新:
@SPIpublic interface SpiTest{
@Adaptive
void mySpi(URL url, String name);
}
在办法上减少注解 @Adaptive,Adaptive就是通知Dubbo应该应用哪一个实现类来调用mySpi办法。实现逻辑就是通过约定在URL(key-value)中提取 key值,通过key值来决定实现类。比方咱们应用 protocol为Dubbo,那么通过yml文件指定dubbo.protocol.name为dubbo,在URL上的格局就是 protocol=dubbo,后续执行服务导出 export的时候就会加载DubboProtocol来实现。
@SPI("dubbo") //默认dubbo
public interface Protocol {
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}
配置文件
filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol
org.apache.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=org.apache.dubbo.rpc.protocol.thrift.ThriftProtocol
native-thrift=org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol
memcached=org.apache.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=org.apache.dubbo.rpc.protocol.redis.RedisProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
xmlrpc=org.apache.dubbo.xml.rpc.protocol.xmlrpc.XmlRpcProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
registry=org.apache.dubbo.registry.integration.RegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
再来看咱们的例子:
public class SpiWrapper implements SpiTest{
@Override
public void mySpi(URL url,String name) {
//次要通过在Url上找到 spi.test 参数的值 , ExtensionLoader.getExtensionLoader(SpiTest.class).getExtension("S1")
SpiTest spiTest = ExtensionLoader.getExtensionLoader(SpiTest.class).getAdaptiveExtension();//通过url参数获取自适应对象
//SpiTest spiTest = ExtensionLoader.getExtensionLoader(SpiTest.class).getExtension(url.getParameter("spi.test"));
spiTest.mySpi(url,name);
}
public static void main(String[] args) {
URL url = new URL("dubbo","123",999);
url = url.addParameter("spi.test","S1");//指定url参数
SpiWrapper spiWrapper = new SpiWrapper();
spiWrapper.mySpi(url,"tudou");
}
}
通过指定URL的参数,就能够主动加载对应的扩大实现类。具体的ExtensionLoader源码剖析,官网写的很具体有趣味能够看一下。
Dubbo源码中大量应用了SPI的动静扩大,如果不弄清楚,可能对学习源码会是一个比拟大的妨碍。心愿这篇文章能帮忙大家进一步的了解SPI的自适应,在进行源码调试的时候,能够追踪到具体的实现类。