• dubbo源码分析1——SPI机制的概要介绍


    插件机制是Dubbo用于可插拔地扩展底层的一些实现而定制的一套机制,比如dubbo底层的RPC协议、注册中心的注册方式等等。具体的实现方式是参照了JDK的SPI思想,由于JDK的SPI的机制比较简单,满足不了一些复杂的需求,所以dubbo重写了一套SPI机制,实现了类似spring的IOC和AOP的机制,灵活度和扩展性大大得以提升,这套机制很有用,我们如果能深刻理解,完成可以把它从dubbo的代码中剥离出来,用在自己系统中。这篇文章先简单讲一下这套机制:

    在dubbo的jar内部的/META-INF/dubbo/internal下会有许多以接口名命名的文件,如图:

    这些文件正是用于扩展用途的,我们称之为SPI描述文件。每个文件就代表一种扩展,文件打开后,在文件中会以行为单位配置该扩展接口的具体实现类,每个扩展可以有多个实现,以key-value的形式进行配置。例如打开/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,其中的每一行都是com.alibaba.dubbo.rpc.Protoco接口的一个实现,内容如下图:

    在使用时,Dubbo提供了两个类,一个是com.alibaba.dubbo.common.extension包下的ExtensionLoader类,另一个是com.alibaba.dubbo.common.extension包下的ExtensionFactory,使用的代码如下:

      例如我们想通过插件机制获取某个接口扩展类,以com.alibaba.dubbo.rpc.Protocol为例:

    @Test
        public void getExtensionLoader(){
            ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class) ;
            
            Protocol protocol = (Protocol) extensionLoader.getExtension("dubbo") ;
            
        }

    我们对上面的单元测试做个简单分析,我们想获取某个SPI接口实例其实就两步:

    1. 获取相应的SPI接口的ExtensionLoader实例

    2. 通过ExtensionLoader实例获取相应的SPI接口实例

    而要明白原理,则需要搞清以下4个问题就可以

    1.  每个Dubbo的SPI接口上都要有SPI的类注解

    2.  每个Dubbo的SPI接口的具体实例,可通过调用其所属的ExtensionLoader实例的getExtension方法来获取:

    3. 每个Dubbo的SPI接口的ExtensionLoader实例又是如何产生的呢

    4. 每个ExtensionLoader实例中的实例属性objectFactory(类型为ExtensionFactory)的作用

    以下摘抄出相应的代码来问答上述问题,代码是最好的答案:

    代码的执行调用顺序:

      getExtension方法——>createExtension方法——>getExtensionClasses方法——>loadExtensionClasses方法——>loadFile方法

    //这段代码用来回答上述第2个问题
        public T getExtension(String name) {
            if (name == null || name.length() == 0)
                throw new IllegalArgumentException("Extension name == null");
            if ("true".equals(name)) {
                return getDefaultExtension();
            }
            Holder<Object> holder = cachedInstances.get(name);
            if (holder == null) {
                cachedInstances.putIfAbsent(name, new Holder<Object>());
                holder = cachedInstances.get(name);
            }
            Object instance = holder.get();
            if (instance == null) {
                synchronized (holder) {
                    instance = holder.get();
                    if (instance == null) {
                        instance = createExtension(name);  //这句才是关键
                        holder.set(instance);
                    }
                }
            }
            return (T) instance;
        }
    private T createExtension(String name) {
            Class<?> clazz = getExtensionClasses().get(name);   //要想获取实例,就先要找出类信息,就好比要想富先修路
            if (clazz == null) {
                throw findException(name);
            }
            try {
                T instance = (T) EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                    instance = (T) EXTENSION_INSTANCES.get(clazz);
                }
                injectExtension(instance);  //此处将要完成注入工作,这跟Spring的Bean创建的过程类似,要完成一些依赖注入工作,在后面也能看到ExtensionFactory的作用
           

            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
              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);
         }

    }
     // getExtensionClasses方法的实质性代码。
        private Map<String, Class<?>> loadExtensionClasses() {
            final SPI defaultAnnotation = type.getAnnotation(SPI.class);
            if(defaultAnnotation != null) {
                String value = defaultAnnotation.value();
                if(value != null && (value = value.trim()).length() > 0) {
                    String[] names = NAME_SEPARATOR.split(value);
                    if(names.length > 1) {
                        throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                                + ": " + Arrays.toString(names));
                    }
                    if(names.length == 1) cachedDefaultName = names[0];
                }
            }
            
            Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
            loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
            loadFile(extensionClasses, DUBBO_DIRECTORY);
            loadFile(extensionClasses, SERVICES_DIRECTORY);
            return extensionClasses;
        }

    //这段代码回答上述第3个问题

    //ExtensionLoader的构造方法  
    private ExtensionLoader(Class<?> type) {
            this.type = type;
            objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
        }

    //此代码说明 ExtensionFactory 的作用,即上述第4个问题

    private T injectExtension(T instance) {
            try {
                if (objectFactory != null) {
                    for (Method method : instance.getClass().getMethods()) {
                        if (method.getName().startsWith("set")
                                && method.getParameterTypes().length == 1
                                && Modifier.isPublic(method.getModifiers())) {
                            Class<?> pt = method.getParameterTypes()[0];
                            try {
                                String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                                Object object = objectFactory.getExtension(pt, property);
                                if (object != null) {
                                    method.invoke(instance, object);
                                }
                            } catch (Exception e) {
                                logger.error("fail to inject via method " + method.getName()
                                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            return instance;
        }

     总结:本文只是对Dubbo的SPI机制做一个粗的说明,其实一句话就能说清楚,动态获取SPI接口的实例 ,最简单的方式就是调用 ExtensionLoader实例 的 T getExtension(String name)方法就可以,传入的参数就是上述SPI描述文件中的key ,而 ExtensionFactory 的作用就是为了完成SPI实例间的依赖注入。后面我们还会针对Dubbo的SPI机制做更深入的分析。

  • 相关阅读:
    cordova封装h5为app,cookie不可用解决方法
    Cordova环境搭建
    HelloWorld
    《机器学习实战》-朴素贝叶斯
    《机器学习实战》-决策树
    《机器学习实战》-k近邻算法
    《机器学习实战》-机器学习基础
    单输出感知机及其梯度
    损失函数及其梯度
    激活函数及其梯度
  • 原文地址:https://www.cnblogs.com/hzhuxin/p/7520506.html
Copyright © 2020-2023  润新知