dubbo SPI

什么是spi

dubbo的所有功能都拆分并且抽象为interface,通过SPI查找这些interface的实现,并且通过url组合起来,就成了一个完整的rpc框架。SPI可理解为根据接口,找不同的实现类。

为什么要重新造一个

那么JDK已经有了SPI,dubbo为何要自己写一个?有没比SPI更好的机制去加载接口的实现类?

前面我们写了一个JDK的SPI例子,那么它有什么不好呢?

1.JDK中spi是一次性加载所有扩展,不能按需,有点浪费。 2.dubbo增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点

背景知识

扩展点自动包装

ExtensionLoader.getExtension()返回不一定是扩展点,有可能返回扩展点实现类的包装类Wrapper。
那什么时候会出现这种情况呢?

只有当该class无adative注解,并且构造函数包含目标接口(type)类型   
例如:ProtocolFilterWrapper,ProtocolListenerWrapper.它会存在cachedWrapperClasses中。    
复制代码

这其实是AOP的思想。比如下面这样,我们可在调用实际方法前后做一些额外操作。可以将一些公共逻辑抽取出来,放在wrapper类中。

public class XxxProtocolWrapper implemenets Protocol {
    Protocol impl;
    public XxxProtocol(Protocol protocol) { impl = protocol; }
    // 接口方法做一个操作后,再调用extension的方法
    public void refer() {
        //... 一些操作
        impl.refer();
        // ... 一些操作
    }
}
复制代码

扩展点自动装配
加载扩展点时,自动注入依赖的扩展点。当扩展点实现类的成员是其他扩展点,那么ExtensionLoader会自动注入依赖的扩展点。
这不就是ioc嘛。!!!!
那么它是怎么做到的呢?
它通过扫描扩展点的所有Setter方法来判定其成员。那么如果这个依赖的扩展点有多个实现,该用哪个呢? 这就引出了了扩展点自适应。

扩展点自适应
在上面装备的过程中,其实注入的依赖是一个Adative实例。直到扩展点方法执行时才决定调用是一个扩展点实现。
那么Adative实例是怎么生成的呢?它又是怎么拿到具体对象的呢?
看下面的分析。
扩展点自动激活

本文讨论dubbo SPI机制中ExtensionLoader.getExtensionLoade(Class type)方法 1.dubbo SPI机制是什么?有什么用?怎么实现的? 2.与JDK SPI机制的对比 3.

ExtensionLoader.getExtensionLoader(Classtype)本质就是根据传入的形参,获取一个该类型的ExtensionLoader,然后缓存到EXTENSION_LOADERS,下次直接get。 举个例子:type为com.alibaba.dubbo.container.Container-----> com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.container.Container] dubbo SPI

 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        //...去掉了if判断逻辑
 ExtensionLoader<T> loader =  EXTENSION_LOADERS.get(type);
        if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(
                type, new ExtensionLoader<T>(type));//私有构造方法

            loader =  EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
     private ExtensionLoader(Class<?> type) {
        this.type = type;//扩展点的接口类型
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
        .getAdaptiveExtension());
    }
复制代码

ExtensionLoader的构造方法是私有的,涉及到单例模式。 在这个构造方法中,首先会判断type是否是ExtensionFactory,若是,objectFactory=null。否则获取ExtensionFactory的扩展。
只要不是ExtensionFactory都会走到这里,得到ExtensionFactory的adaptive扩展(AdaptiveExtensionFactory)。这样一个ExtensionLoader就new好了。
每个ExtensionLoader包含type,objectFactory两个元素。type是对应的接口,objectFactory就是定值。(AdaptiveExtensionFactory)

objectFactory这个变量到底是什么呢? 1.在ExtensionLoader是这样定义的;

 private final ExtensionFactory objectFactory
复制代码

它是一个ExtensionFactory。

2.通过
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取。

3.它就是为dubbo的IOC提供所有对象。

下面我们以Protocol为例,debug下代码


这里我们拿到了protocol的ExtensinoLoader。

下面获取adaptive


此时,我们已经拿到了extensionClasses.

因为Protocol没有对应实现类中含有@Adapive注解,但是Protocol的接口方法含有@Adapive注解,所以会走到这一步。

这里会根据定义好的模版生成代码。即生成有@Adapive注解的方法


下面开始就是发生ioc的地方。遍历动态生成的类,找到以set开头的方法。如果有,则注入AdaptiveExtensionFactory对象。这里设计的很巧妙。注入的是一个万能的对象,等到真正需要时,在决定要哪个。

到这里我们的getAdaptiveExtension就返回了。返回的是

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {

    private static final org.apache.dubbo.common.logger.Logger logger = org.apache.dubbo.common.logger.LoggerFactory
        .getLogger(ExtensionLoader.class);
    private java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(0);

    public void destroy() {
        throw new UnsupportedOperationException(
            "method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException(
            "method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
        throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        }
        if (arg0.getUrl() == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        }
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + url.toString()
                    + ") use keys([protocol])");
        }
        org.apache.dubbo.rpc.Protocol extension = null;
        try {
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                .getExtension(extName);
        } catch (Exception e) {
            if (count.incrementAndGet() == 1) {
                logger.warn("Failed to find extension named " + extName
                    + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", e);
            }
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                .getExtension("dubbo");
        }
        return extension.export(arg0);
    }

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
        throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) {
            throw new IllegalArgumentException("url == null");
        }
        org.apache.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + url.toString()
                    + ") use keys([protocol])");
        }
        org.apache.dubbo.rpc.Protocol extension = null;
        try {
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        } catch (Exception e) {
            if (count.incrementAndGet() == 1) {
                logger.warn("Failed to find extension named " + extName
                    + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", e);
            }
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension("dubbo");
        }
        return extension.refer(arg0, arg1);
    }
}
复制代码

下面我们看如何根据name,获取Extension。


?,首先看InjvmProtocol中是否有set方法,如有,则注入Adaptive类。
继续。这里Protocol接口有两个wrapperClasses实例。它的作用是完成Aop。我们看下官网的解释。

对应这里,就是每个wrapperClasses实例,都注入Protocol的实现.
for循环。第一步将InjvmProtocol放到ProtocolListenerWrapper中。 第二步,为ProtocolListenerWrapper放入adaptive

继续循环,进入到 ProtocolFilterWrapper。这里将会给第一步得到的ListenerWrapper包装。

然后,注入adaptive.
现在我们拿到了instance.整个流程走完了。不容易。设计的太巧妙了。

转载于:https://juejin.im/post/5a51992df265da3e2839d4f0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值