5

dubbo spi机制

 3 years ago
source link: https://segmentfault.com/a/1190000041212860
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

dubbo spi机制

发布于 今天 10:52

框架设计的有弹性扩展性好会给使用者带来很大的方便,如果业务有新的变化只要使用新的实现去替换之前的实现,且框架如何去加载实现我们也不用太关注,显然这对我们开发者来说是非常友好的。java里的SPI是这个思想,dubbo里的SPI同样是这个思想,关于java里的spi因为会一次性将扩展点逻辑都执行而显得不够灵活,所以这里不再展开(可以参考java spi),这里重点说下dubbo里的SPI.

使用方式
1,在META-INF/services目录下根据接口的全路径名创建一个文件,文件内容为key=value的形式
2,ExtensionLoader.getExtensionLoader(xxx.class).getExtension("key")这样就可以获取到我们自定义的实现了。

实现分析
下面我们对ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name)这行代码展开下。

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {

        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        //判断给定type是否为接口
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        //判断给定接口上是否有SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //先从map里获取ExtensionLoader,如果没有进行创建
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

这全方法主要是获取ExtensionLoader:
1,对给定的type进行判断,一是判断是否是接口类型;二是判断接口上是否有SPI注解
2,根据给定的type先从Map获取对应的ExtensionLoader,要是没有进行创建。

接下来我们再看看getExtension(name)方法:

public T getExtension(String name) {

        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //先获取包装类Holder
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        //再从包装类里获取实现类的实例,需要重点看下createExtension
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

getExtension(name)方法逻辑清晰明了:
1,先获取包装类Holder
2,从包装类Holder里获取具体实现类的实例。
实现类的实例是如何创建的呢?再看下createExtension(name)

private T createExtension(String name) {

        /**
         * getExtensionClasses会加载META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/这三
         * 个目录下的所有类
         */
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //通过反射的方式创建name对应的实例,并放到map里,下次可以直接从map里取
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            /**
             * 如果实现类对象里有set方法,则框架还会根据依赖对象的实际情况进行自动注入的工作
             * 说明,只有满足下面两个条件才会进行注入:
             * 1,被依赖的对象也是在services,dubbo,internal这三个目录下
             * 2,set方法上没有使用了DisableInject注解
             */
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

这方法的主要逻辑:
1,通过Class.forName加载META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/这三个目录下的所有类
2,通过反射创建type对应的实例
3,如果符合注入条件,框架还会进行自动注入。

最后用一张图总结下整个过程:

创建完对象后都会将对象放入缓存方便下次直接获取。


Recommend

  • 87
    • blueskykong.com 6 years ago
    • Cache

    Java SPI机制详解

    Java SPI机制详解

  • 15
    • my.oschina.net 4 years ago
    • Cache

    SPI 在 Dubbo中 的应用

    通过本文的学习,可以了解 Dubbo SPI 的特性及实现原理,希望对大家的开发设计有一定的启发性。 SPI 全称为 Service Provider Interface,是一种模块间组件相互引用的机制。其方案通常是提供方将接口实现类的全名配置在classPath下的指定文件中,由...

  • 14

    Dubbo是一款开源的、高性能且轻量级的Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用、智能容错和负载均衡,以及服务自动注册和发现。Dubbo最早是阿里公司内部的RPC框架,于 2011 年开源,之后迅速成为国内该类开源项目的佼佼者,2018年2...

  • 15
    • 微信 mp.weixin.qq.com 4 years ago
    • Cache

    Java SPI (Service Provider Interface) 机制详解

    概述 关键词:解耦,可拔插,面向接口编程,动态类加载。 本质:Java SPI 实际上是“基于接口的编程+策略模式+约定配置文件...

  • 5

    先点赞再看,养成好习惯SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口...

  • 12
    • my.oschina.net 3 years ago
    • Cache

    详解Apache Dubbo的SPI实现机制

    一、SPI SPI全称为Service Provider Interface,对应中文为服务发现机制。SPI类似一种可插拔机制,首先需要定义一个接口或一个约定,然后不同的场景可以对其进行实现,调用方在使用的时候无需过多关注具体的实现细节。在Java中,SPI...

  • 5

    SPI 全称为 Service Provider Interface,是一种服务发现机制。当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类。所以在程序中并没有直接指定使用接口的哪个实现,而是在外部进行装配。要想了解 Dubbo 的设计与实现,其中 Dubbo SPI...

  • 6
    • mikeygithub.github.io 3 years ago
    • Cache

    源码学习-Dubbo SPI 源码分析

    今天我们来聊一聊 Dubbo 的 SPI Java 的 SPI 见 https://mikeygithub.github.io/2021/07/21/yuque/coahg1/ SPI : Service Porvider Interface...

  • 11
    • www.cnblogs.com 2 years ago
    • Cache

    Dubbo-SPI机制 - 大魔王先生

    SPI全称为Service Provider Interface,是Java提供的一种服务发现机制。SPI的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。 正因此特性,我们可以很容易的通过SPI机制为我们的程序提...

  • 7

    Dubbo2.7的Dubbo SPI实现原理细节 总结/朱季谦

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK