dubbo:学习整理0——扩展机制

一,spi基础

Spi 即service provicer interface 服务接口  可以有效的实现解耦和自定义;

Java中的spi即根据参数来选择需要的扩展类,扩展类即为实现类,java中使用ServiceLoader来加载

使用方式:

  1. 定义接口 test.boot.provider.spi.IspiTest 有一个方法hello
  2. 实现该接口 两个实现类test.boot.provider.spi.SpiTestImpl和test.boot.provider.spi.SpiTestImplB 在hello方法中分别打印
  3. 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件test.boot.provider.spi.IspiTest

里面的内容为实现类全路径,每行写一个

test.boot.provider.spi.SpiTestImpl

test.boot.provider.spi.SpiTestImplB

  1. 使用:  使用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. 》》1,ExtensionLoader.getExtensionLoader(Protocol.class)  进入到静态方法,type=Protocol,由于静态缓存EXTENSION_LOADERS中没有该type对应的extensionLoader扩展加载器,
  2. >>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()

  1. 先判断类是否有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来完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值