一,spi基础
Spi 即service provicer interface 服务接口 可以有效的实现解耦和自定义;
Java中的spi即根据参数来选择需要的扩展类,扩展类即为实现类,java中使用ServiceLoader来加载
使用方式:
- 定义接口 test.boot.provider.spi.IspiTest 有一个方法hello
- 实现该接口 两个实现类test.boot.provider.spi.SpiTestImpl和test.boot.provider.spi.SpiTestImplB 在hello方法中分别打印
- 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件test.boot.provider.spi.IspiTest
里面的内容为实现类全路径,每行写一个
test.boot.provider.spi.SpiTestImpl
test.boot.provider.spi.SpiTestImplB
- 使用: 使用ServiceLoader
ServiceLoader<ISpiTest> spiTests=ServiceLoader.load(ISpiTest.class);
for(ISpiTest test:spiTests){
test.hello();
}
运行结果:
我是实现类a
我是实现类b
应用程序可以根据实际业务情况启用框架扩展或替换框架组件。如选择SpiTestImpl或SpiTestImplB中的一个
二,dubbo中的spi概述
Dubbo中spi
扩展点:即为接口类 如Protocol类 使用@Spi注解,方法使用@Adaptive注解,说明是一个自适应的方法,会为方法生成对应的代码;
扩展:即为接口的实现类,如DubboProtocol类,
系统会根据配置的参数来选择所需要的扩展类对象
如设置dubbo.protocol.name=rmi
则在获取扩展类对象时返回RmiProtocol对象
或者自定义一个协议实现类,然后配置,就可以使用自定义的协议;
三,dubbo中扩展机制
ServiceBean 继承于ServiceConfig
在ServiceConfig中包含静态属性Protocol
private static final Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
ExtensionLoader扩展加载器,用来加载type对应的扩展类信息
public ExtensionLoader static getExtensionLoader(type){
省略。。
ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
}
return loader;
}
private ExtensionLoader(Class<?> type) {
this.type = type;
this.objectFactory = type == ExtensionFactory.class ? null : (ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();
}
过程1——加载type=Protocol的扩展加载器
- 》》1,ExtensionLoader.getExtensionLoader(Protocol.class) 进入到静态方法,type=Protocol,由于静态缓存EXTENSION_LOADERS中没有该type对应的extensionLoader扩展加载器,
- >>1调2, new ExtensionLoader(Protocol),new一个加载器
在此方法中type=Protocol!=ExtensionFactory,则调用getExtensionLoader(ExtensionFactory.class)
所以进入到type=ExtensionFactory的扩展类加载过程
过程2——加载type=ExtensionFactory的扩展加载器——开始
ExtensionLoader.getExtensionLoader(ExtensionFactory.class),由于静态缓存EXTENSION_LOADERS中没有type=ExtensionFactory对应的extensionLoader扩展加载器,新建一个加载器
>>调用new ExtensionLoader(ExtensionFactory),由于此时type=ExtensionFactory,则objectFactoyr=null,new完成完成。
并将type=ExtensionFactory的加载器存入到缓存中,并返回new出来的扩展类加载器
过程2——加载type=ExtensionFactory的扩展加载器——结束
此时得到的是type=ExtensionFactory的扩展类加载器loader,回到过程1,再调用loader.getAdaptiveExtension()方法获取自适应扩展对象
3, >> 2调用3 ,loader.getAdaptiveExtension(),从缓存cachedAdaptiveInstance中取,如果有,则直接返回;如果没有,则创建一个自适应扩展对象createAdaptiveExtension(),并放入到缓存中cachedAdaptiveInstance。
4,>> 3调用4,createAdaptiveExtension()创建自适应扩展对象,先获取自适应扩展类class(5到10),然后newInstance()创建其对象,再injectExtension()对对象进行属性设置。
5,>>4调用5 ;getAdaptiveExtensionClass() 获取自适应扩展类,先获取扩展类(包含自适应扩展类获取),然后判断cachedAdaptiveClass 自适应扩展类缓存有没有值,如果有值则直接返回;如果没有值则创建自适应类,createAdaptiveExtensionClass(),然后返回自适应扩展类。
6,>>5调用6,getExtensionClasses(),从缓存中cachedClasses取,如果有直接返回;如果没有,则进行加载扩展类,loadExtensionClasses(),加载完后放入缓存cachedClasses中,这个缓存是一个Holder<Map<String, Class<?>>> 类别,可能有多个所以用map。
7,>>6调用7,loadExtensionClasses()中,先获取spi注解类的默认值, SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
如果有@spi注解有value值,则当前加载类的cachedDefaultName属性设置为此value值;ExtensionFactory类没有value值,所以不设置;比如Protol类就有value值为“dubbo”;
进入加载扩展类信息流程,可能有多个所以使用map存储,加载完成后返回该map
Map<String, Class<?>> extensionClasses = new HashMap();
this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/");
this.loadDirectory(extensionClasses, "META-INF/dubbo/");
this.loadDirectory(extensionClasses, "META-INF/services/");
8,>>7调用8 loadDirectory
,9,》loadResource(),
加载的配置文件为String fileName = dir + this.type.getName();
比如加载ExtensionFactory类的扩展类的路径就为:
dubbo包中 META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory类名文件中配置消息为
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory //自适应的扩展类,有@Adpative注解
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
10 》loadClass()
- 先判断类是否有Adaptive.class注解,如果有说明是自适应的类,并如果当前自适应类缓存cachedAdaptiveClass 没有值,则 cachedAdaptiveClass 设置为该值;
比如ExtensionFactory中有AdaptiveExtensionFactory, 缓存在 cachedAdaptiveClass 中;
比如Protocol中没有自适应的扩展类,cachedAdaptiveClass为null。
2)再判断是否是一个包装类,isWrapperClass(clazz)》clazz.getConstructor(this.type);,即有type类型对象为参数的构造函数,即为wrapper(type的包装类),则放入对象缓存cachedWrapperClasses中;比如ExtensionFactory中没有;比如Protocol中ProtocolListenerWrapper的。
3)以上都不满足则加入到extensionClasses 中。
加载完成后返回的map为<“Spring”, SpringExtensionFactory>、<“Spi”, SpiExtensionFactory>
11,4调用11,在获取自适应扩展类之后,创建自适应扩展对象class.newInstance()
12,11调用12,创建AdaptiveExtensionFactory 的实例adaptiveExtensionFactory ,构造函数
取type为ExtensionFactory的extensionLoader,静态缓存中存在,直接返回;
取其支持的扩展类,通过extensionLoader对象的getExtensionClasses()方法,即extensionLoader对象之前存在缓存cachedClasses中的值,然后遍历可支持扩展类,并创建(loader.getExtension(name))扩展类对象即依次实例化SpiExtensionFactory和SpringExtensionFactory,存于实例adaptiveExtensionFactory 的属性factories中
12.1,12,调用12.1 loader.getExtension(name),如果name=”true”返回默认的扩展实例;先从缓存中去,cachedInstances.get(name),如果缓存中没有对应key的实例,创建一个并加入缓存,createExtension(name)
先取到name对应的扩展类,然后从静态缓存EXTENSION_INSTANCES取得对象实例,如果EXTENSION_INSTANCES中没有直接newInstance()创建实例instance并存入缓存EXTENSION_INSTANCES,然后对实例进行属性设置,最后判断是否有缓存包装类cachedWrapperClasses,有的话迭代进行如下操作:以instance为参数,实例化包装类,并将包装类对象赋值给instance,然后injectExtension()设置对象属性,得到最后的instance就是一个包装类的链。
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for(Iterator i$ = wrapperClasses.iterator();
i$.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
wrapperClass = (Class)i$.next();
}
}
如protocol中传入的name=registry,存在cachedWrapperClasses,扩展配置文件中有三个包装类ProtocolListenerWrapper 、QosProtocolWrapper 、ProtocolFilterWrapper 三个包装类,依次进行实例化,返回最后一个实例(ProtocolFilterWrapper),在实例化 wrapper 的时候,以上一个 wrapper 实例作为构造函数的入参,于是得到 ProtocolFilterWrapper 实例拥有 QosProtocolWrapper 实例,QosProtocolWrapper 实例拥有 ProtocolListenerWrapper 实例,而 ProtocolListenerWrapper 实例拥有 RegistryProtocol 实例。即 ProtocolFilterWrapper -> QosProtocolWrapper -> ProtocolListenerWrapper -> RegistryProtocol
13,4调用13 ,injectExtension()设置对象属性
14,3调用14 adaptiveExtensionFactory 实例存到缓存中cachedAdaptiveInstance中
过程1 获取type=ExtensionFactory的自适应扩展对象——结束
15,1调用15,将type为protocol的extensionLoader对象,放入缓存,并返回extensionLoader对象。,
所以:type为protocol的extensionLoader对象中的objectFactory属性值为adaptiveExtensionFactory 实例
总结:第一次使用ExtensionLoader.getExtensionLoader(type)方法来加载某个类别的扩展加载器时,会对type=ExtensionFactory的扩展加载器进行初始化;以后再次调用的时候便可直接使用type=ExtensionFactory对象;
即:所有类别的extensionLoader对象中的objectFactory属性值均为为adaptiveExtensionFactory 实例。
获取到type为protocol的extensionLoader对象后,获取protocol自适应扩展对象getAdaptiveExtension()
过程同获取ExtensionFactory自适应扩展对象类型。
其他步骤都一样,不一样的是加载配置文件的地址
加载的配置文件为String fileName = dir + this.type.getName();
dubbo包中 META-INF/dubbo/internal/com.alibaba.dubbo.rpc文件中,有如下的配置信息,这些都称为扩展类
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
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=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
由于没有自适应类扩展类,则需要创建,创建的是字节码文件
createAdaptiveExtensionClass()
注意:自适应实现只能有一个,有两种方式
一种是通过配置文件,这种就是针对@Adaptive注解在类级别的时,如AdaptiveExtensionFactory。
另外一种是@Adaptive注解在方法级别时,自适应实现类就需要通过字符码动态生成,如Protocol的export和refer方法。
创建出来的Protocol自适应扩展对象为
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
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!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
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 com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
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);
}
}
假如在服务暴露过程中,当程序调用到达protocol.export()方法是,调用的其实是上述代码中的export()方法,会执行该方法中的如下代码,arg0值是wrapperInvoker,extName值是”registry”,
com.alibaba.dubbo.rpc.Protocol extension =
(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
发现又进入到了获取扩展类的环节
获取protocol的扩展类加载器,前面已经获取了,直接调用getExtension(registry)
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
》createExtension(String name) 前面记录过
扩展配置文件中有三个包装类,依次进行实例化,返回最后一个实例(ProtocolFilterWrapper),在实例化 wrapper 的时候,以上一个 wrapper 实例作为构造函数的入参,于是得到 ProtocolFilterWrapper 实例拥有 QosProtocolWrapper 实例,QosProtocolWrapper 实例拥有 ProtocolListenerWrapper 实例,而 ProtocolListenerWrapper 实例拥有 RegistryProtocol 实例。即 ProtocolFilterWrapper -> QosProtocolWrapper -> ProtocolListenerWrapper -> RegistryProtocol
获取到了扩展类,执.行export()方法,即ProtocolFilterWrapper.export(),由于包装类中export()方法链式调用,会依次调用 ProtocolFilterWrapper -> QosProtocolWrapper -> ProtocolListenerWrapper -> RegistryProtocol 中的export()方法
在RegistryProtocol 中的export()方法方法中,具体执行如下内容:
//具体的协议去暴露服务
ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//得到具体的注册中心,连接注册中心,此时提供者作为消费者引用注册中心核心服务RegistryService
Registry registry = getRegistry(originInvoker);
//获取要注册到注册中心的url
URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//调用远端注册中心的register方法进行服务注册,若有消费者订阅此服务,则推送消息让消费者引用此服务
registry.register(registedProviderUrl);
//提供者向注册中心订阅所有注册服务的覆盖配置
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//返回暴露后的Exporter给上层ServiceConfig进行缓存
return new Exporter<T>() {。。。}
总结:所有扩展类的加载过程都是类似,就是看meta-info下的类文件名中配置的类,最后由谁来执行具体的方法,一般通过项目配置文件来配置,如Protocol如DubboProtocol来完成