写点什么

【Dubbo 源码】SPI 机制源码解析

  • 2022-11-14
    江西
  • 本文字数:17025 字

    阅读完需:约 56 分钟

【Dubbo源码】SPI机制源码解析

Part1 什么是 SPI 机制

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。接下来,我们先来了解一下 Java SPI 与 Dubbo SPI 的用法,然后再来分析 Dubbo SPI 的源码。

Part2SPI 示例

Part3Java SPI 示例

前面简单介绍了 SPI 机制的原理,本节通过一个示例演示 Java SPI 的使用方法。首先,我们定义一个接口,名称为 Robot。

public interface Robot {    void sayHello();}
复制代码

接下来定义两个实现类,分别为 OptimusPrime 和 Bumblebee。

public class OptimusPrime implements Robot {        @Override    public void sayHello() {        System.out.println("Hello, I am Optimus Prime.");    }}
public class Bumblebee implements Robot {
    @Override    public void sayHello() {        System.out.println("Hello, I am Bumblebee.");    }}
复制代码

接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 org.apache.spi.Robot。文件内容为实现类的全限定的类名,如下:

org.apache.spi.OptimusPrimeorg.apache.spi.Bumblebee
复制代码

做好所需的准备工作,接下来编写代码进行测试。

public class JavaSPITest {
    @Test    public void sayHello() throws Exception {        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);        System.out.println("Java SPI");        serviceLoader.forEach(Robot::sayHello);    }}
复制代码


image.png

image.png

从测试结果可以看出,我们的两个实现类被成功的加载,并输出了相应的内容。关于 Java SPI 的演示先到这里,接下来演示 Dubbo SPI。

Part4Dubbo SPI 示例

Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下。

optimusPrime = org.apache.spi.OptimusPrimebumblebee = org.apache.spi.Bumblebee
复制代码

与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。下面来演示 Dubbo SPI 的用法:

public class DubboSPITest {
    @Test    public void sayHello() throws Exception {        ExtensionLoader<Robot> extensionLoader =             ExtensionLoader.getExtensionLoader(Robot.class);        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");        optimusPrime.sayHello();        Robot bumblebee = extensionLoader.getExtension("bumblebee");        bumblebee.sayHello();    }}
复制代码


image.png

image.png

Part5Dubbo SPI 机制源码分析

SPI 机制的实现类是 ExtensionLoader;所以主要本篇文章主要分析这个类的源码;

Part6ExtensionLoader 静态类和静态方法

如果你有留心 dubbo 使用 SPI 机制的时候,无非大部分都是通过一个 static 静态方法来调用的,而且有很多的静态属性来保存全局的 SPI 实例;我们先了解一下这些静态方法和属性

静态属性

    //文件路径-> (以接口类名为文件名,文件内容为实现类) 一般这个里面存放自定义服务相关类    private static final String SERVICES_DIRECTORY = "META-INF/services/";    //文件路径-> (以接口类名为文件名,文件内容为实现类) 一般这个里面存放dubbo相关的类    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";    //文件路径-> (以接口类名为文件名,文件内容为实现类) 这个存放的就是dubbo框架自身的类    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";    //分隔符    private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");   //存放所有需要扩展的接口类名,和对应的ExtensionLoader扩展加载器    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();    
复制代码

前面三个文件夹路径 SERVICES_DIRECTORY、DUBBO_DIRECTORY、DUBBO_INTERNAL_DIRECTORY 本质上没有区别,因为这三个路径都会被 dubbo 扫描一遍,把这些文件夹下面的文件全部加载到内存中;

ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS 这个 Map 是用来存放所有的 SPI 的管理的 Class 和 ExtensionLoader 扩展器的;

比如 dubbo 内置的

image.png

image.png

image.png

image.png

上面的这些文件名都是一个 interface 的全类名路径;那么我们的 EXTENSION_LOADERS 中的 key 就对应这些 interface,value 就对应一个单独的 ExtensionLoader 扩展器;

ConcurrentMap, Object> EXTENSION_INSTANCES

TODO…

静态方法

    //是否有SPI注解    private static <T> boolean withExtensionAnnotation(Class<T> type) {        return type.isAnnotationPresent(SPI.class);    }
    @SuppressWarnings("unchecked")    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {        if (type == null)            throw new IllegalArgumentException("Extension type == null");        if (!type.isInterface()) {            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");        }        if (!withExtensionAnnotation(type)) {            throw new IllegalArgumentException("Extension type(" + type +                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");        }
        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;    }    //获取ClassLoader    private static ClassLoader findClassLoader() {        return ExtensionLoader.class.getClassLoader();    }    
复制代码

主要看上面的 getExtensionLoader 方法,这个是非常关键的一个方法,因为 dubbo 想要获取对应 Class 的一个实例,那么需要先获取这个 Class 的 ExtensionLoader 扩展加载器,这个方法就是对外提供的一个入口;

这个 Class 必须是一个 interface,必须打上了 SPI 的注解

我们发现获取这个 ExtensionLoader 扩展加载器 是从全局静态变量 EXTENSION_LOADERS 获取的;但是一开始没有的情况,需要先实例化一个 ExtensionLoader 扩展加载器 出来 new ExtensionLoader(type) ;

为了方便分析源码,我们一起启动服务,开启 Debug;Dubbo 最先调用 ExtensionLoader 的是

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

image.png

image.png

image.png

image.png

1new ExtensionLoader(type) 实例化扩展加载器

以首先被加载的Protocol为例

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
复制代码

最终是执行了new ExtensionLoader(type)的方法;并且保存到了静态属性EXTENSION_LOADERS中;

我们看看是怎么实例化的

    private ExtensionLoader(Class<?> type) {        this.type = type;        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());    }
复制代码

当前的 type=interface com.alibaba.dubbo.rpc.Protocol; 那么这个 ExtensionFactory objectFactory;属性又是什么呢?

TODO…

那么这里要先去执行

ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

这个时候的 type=interface com.alibaba.dubbo.common.extension.ExtensionFactory

当 ExtensionFactory 也 new 了一个 ExtensionLoader 之后,然后去调用方法 getAdaptiveExtension(); 这个方法的作用是

获取自适应扩展 ;

public T getAdaptiveExtension() {        Object instance = cachedAdaptiveInstance.get();        if (instance == null) {            if (createAdaptiveInstanceError == null) {                synchronized (cachedAdaptiveInstance) {                    instance = cachedAdaptiveInstance.get();                    if (instance == null) {                        try {                            instance = createAdaptiveExtension();                            cachedAdaptiveInstance.set(instance);                        } catch (Throwable t) {                            createAdaptiveInstanceError = t;                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);                        }                    }                }            } else {                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);            }        }
        return (T) instance;    }
复制代码

cachedAdaptiveInstance 缓存了自适应扩展的实例类;

createAdaptiveExtension()方法创建了自适应扩展的实例,并存放入 cachedAdaptiveInstance

如果这个自适应扩展实例存在的话就直接返回了

那么,是如何创建 自适应扩展实例的呢?

Part7 如何创建自适应扩展实例


    @SuppressWarnings("unchecked")    private T createAdaptiveExtension() {        try {            return injectExtension((T) getAdaptiveExtensionClass().newInstance());        } catch (Exception e) {            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);        }    }
复制代码

简而言之

先获取自适应扩展的 Class

然后调用 Class 的 newInstance()方法来实例化对象

生成的对象,可能还有一些属性要注入,所以执行了方法 injectExtension 依赖注入;

那么问题又来了

Part8 如何获取自适应扩展的 Class?

实例化之后,如何依赖注入?

如何获取自适应扩展的 Class?

获取自适应扩展的 Class, 得先加载文件夹下面的文件啊,自适应扩展也是 SPI 机制中管理的其中一个比较特殊的类而已;

  private Class<?> getAdaptiveExtensionClass() {        getExtensionClasses();        if (cachedAdaptiveClass != null) {            return cachedAdaptiveClass;        }        return cachedAdaptiveClass = createAdaptiveExtensionClass();    }
复制代码

加载当前 type 中所有的扩展类,(加载的具体详情请看下面)

如果扩展类中有带有注解 @Adaptive,说明是自适应扩展类,直接返回

一个 type 有且只有一个自适应扩展类

如果当前 type 中所有的扩展类中没有找到带有注解 @Adaptive 自适应扩展类的话,就会主动去创建一个自适应扩展类

Part9 如何自动创建自适应扩展类

    private Class<?> createAdaptiveExtensionClass() {        String code = createAdaptiveExtensionClassCode();        ClassLoader classLoader = findClassLoader();        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();        return compiler.compile(code, classLoader);    }
复制代码

如果没有自适应扩展类,则 dubbo 会自动生成一个;

先拼接类

查询当前 type 的所有方法中是否有注解 @Adaptive(是方法中的注解),如果一个都没有的话,那么就会抛出异常;

遍历每一个 method,如果方法中没有注解,则该方法拼接出来的就是直接抛异常说不支持该方法;

拼接的过程太繁琐了,直接给一个拼接之后的例子吧

以 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);    }}
复制代码

拼接这个类最主要的地方在ExtensionLoader.getExtensionLoader(T).getExtension(extName);; 这个extName究竟是多少,拼接的逻辑在下面,那个getNameCode就是最终的 extName

image.png

image.png

拼接完成这个类之后,然后选择某个Compiler来生产字节码;选择Compiler也是通过 SPI 选择的;

实例化之后,如何依赖注入?

上面已经分析完如何获取自适应扩展类; 实例完了之后还没有完成;因为扩展类 里面可能还有设置一些属性;所有还有一个依赖注入的过程

 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;    }
复制代码

如果 objectFactory==null 的情况就直接返回了,不需要依赖注入;什么情况下这个值是 null?,只有 type=com.alibaba.dubbo.common.extension.ExtensionFactory;情况下这个才直接返回了;

如果实例中的方法是 set 开头的并且只有一个入参,并且是 public 权限的,就可以依赖注入了

那么注入的属性从哪里来呢?

Part10 依赖注入的属性从哪里来

我们可以看到是从 Object object = objectFactory.getExtension(pt, property);得到的注入属性,然后执行 method.invoke(instance, object);进行注入;

从扩展工厂类中获取 扩展实例

这个 objectFactory=ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();

dubbo 中的自适应扩展类是 AdaptiveExtensionFactory

@Adaptivepublic class AdaptiveExtensionFactory implements ExtensionFactory {
    private final List<ExtensionFactory> factories;
    public AdaptiveExtensionFactory() {        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();        for (String name : loader.getSupportedExtensions()) {            list.add(loader.getExtension(name));        }        factories = Collections.unmodifiableList(list);    }
    @Override    public <T> T getExtension(Class<T> type, String name) {        for (ExtensionFactory factory : factories) {            T extension = factory.getExtension(type, name);            if (extension != null) {                return extension;            }        }        return null;    }
}
复制代码

loader.getSupportedExtensions()获取到的是除了自适应类、包装类之外的扩展类;那么这个方法得到的名字有两个,①SpiExtensionFactory ②SpringExtensionFactory, 拿到的只是名字而已,那么还要通过 loader.getExtension(name)来拿到对应的实例对象! 具体的创建实例对象细节看后面

在这里的情况,那么 factories 中就有两个扩展实例

1.SpiExtensionFactory SPI 的扩展工厂

2.SpringExtensionFactory Spirng 的扩展工厂

那么获取属性的时候我们看到调用了 Object object = objectFactory.getExtension(pt, property); 执行的方法就是

image.png

image.png

image.png

image.png

可以看到遍历执行SpiExtensionFactorySpringExtensionFactory两个扩展类的getExtension方法;

例如SpiExtensionFactory;

image.png

image.png

所以这个扩展工厂类,我们也可以写自己的扩展工厂类来生成对应的对象来依赖注入对象!

加载当前Type中所有的扩展类

加载扩展类比较重要,所以我单独拉出来细说

    private Map<String, Class<?>> getExtensionClasses() {        Map<String, Class<?>> classes = cachedClasses.get();        if (classes == null) {            synchronized (cachedClasses) {                classes = cachedClasses.get();                if (classes == null) {                    classes = loadExtensionClasses();                    cachedClasses.set(classes);                }            }        }        return classes;    }
    // synchronized in getExtensionClasses    private Map<String, Class<?>> loadExtensionClasses() {        final SPI defaultAnnotation = type.getAnnotation(SPI.class);        if (defaultAnnotation != null) {            String value = defaultAnnotation.value();            if ((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<?>>();        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);        loadDirectory(extensionClasses, DUBBO_DIRECTORY);        loadDirectory(extensionClasses, SERVICES_DIRECTORY);        return extensionClasses;    }
复制代码

如果cachedClasses不为空直接返回;说明已经加载过了,这个就是用来保存当前Class中的所有扩展类名;

image.png

image.png

cachedClasses 的 key 是左边值,value 是右边对应的 Class

如果还没有加载过,则开始加载

如果当前的 type 上的 @SPI 有默认值,例如 @SPI("dubbo"),则将其设置到属性 cachedDefaultName 中;

加载三个文件夹 DUBBO_INTERNAL_DIRECTORY、DUBBO_DIRECTORY、SERVICES_DIRECTORY 下面的对应 type 文件中的具体实现类;

Part11 加载文件中的具体实现类

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {        if (!type.isAssignableFrom(clazz)) {            throw new IllegalStateException("Error when load extension class(interface: " +                    type + ", class line: " + clazz.getName() + "), class "                    + clazz.getName() + "is not subtype of interface.");        }        if (clazz.isAnnotationPresent(Adaptive.class)) {            if (cachedAdaptiveClass == null) {                cachedAdaptiveClass = clazz;            } else if (!cachedAdaptiveClass.equals(clazz)) {                throw new IllegalStateException("More than 1 adaptive class found: "                        + cachedAdaptiveClass.getClass().getName()                        + ", " + clazz.getClass().getName());            }        } else if (isWrapperClass(clazz)) {            Set<Class<?>> wrappers = cachedWrapperClasses;            if (wrappers == null) {                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();                wrappers = cachedWrapperClasses;            }            wrappers.add(clazz);        } else {            clazz.getConstructor();            if (name == null || name.length() == 0) {                name = findAnnotationName(clazz);                if (name == null || name.length() == 0) {                    if (clazz.getSimpleName().length() > type.getSimpleName().length()                            && clazz.getSimpleName().endsWith(type.getSimpleName())) {                        name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();                    } else {                        throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);                    }                }            }            String[] names = NAME_SEPARATOR.split(name);            if (names != null && names.length > 0) {                Activate activate = clazz.getAnnotation(Activate.class);                if (activate != null) {                    cachedActivates.put(names[0], activate);                }                for (String n : names) {                    if (!cachedNames.containsKey(clazz)) {                        cachedNames.put(clazz, n);                    }                    Class<?> c = extensionClasses.get(n);                    if (c == null) {                        extensionClasses.put(n, clazz);                    } else if (c != clazz) {                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());                    }                }            }        }    }    
复制代码

文件中的扩展类必须是当前 type 的实现类

如果扩展类中带有注解 @Adaptive 则表示这个是自适应扩展类;并且缓存到属性 cachedAdaptiveClass 中;如果文件中的扩展类有多个 @Adaptive,则会抛异常,最多只有一个自适应扩展类

如果当前加载的扩展类是一个包装类(如果这个扩展类有一个构造函数并且入参是当前 type 的情况),则将这个包装类加入的一个 Map 属性 cachedWrapperClasses 中; 这个属性保存了所有的包装类;

如果不是自适应扩展类也不是包装类,则将此扩展类放入 map 属性 cachedNames 中;key 是扩展类的 Class,value 是 name;这个就是用来维护一个扩展类有几个名字的;因为这个左边的 name 可以用逗号来分割;

image.png

image.png

image.png

image.png

如果不是自适应扩展类也不是包装类,并且扩展类带有注解 @Activate,则放入 map 属性 cachedActivates 中;key 是 name, value 是注解 Activate

loader.getExtension(name)根据名称获取扩展类实例

前面讲了 自适应扩展类的实例化,还有将各个 Class 加载到内存中;但是这个时候其他的扩展类还没有实例化的;

那么 在加载完扩展类之后,具体是如何将这些扩展类实例化的呢?

可以看到,dubbo 只是加载这些扩展类而已,这个时候并没有去加载这里类并且实例化;只有在需要这些扩展类实例的时候,才会去主动实例化;

  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, clazz.newInstance());                instance = (T) EXTENSION_INSTANCES.get(clazz);            }            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);        }    }
    
复制代码

如果 name=true,则实例化默认的扩展类;就是之前加载过程中赋值 cachedDefaultName 的扩展类,也就是 @SPI("默认")里面的值

开始创建实例 createExtension,通过 name 找到对应的 Class,然后调用 clazz.newInstance()进行实例化;将实例化对象存到静态全局变量 EXTENSION_INSTANCES 中;

调用 injectExtension 进行依赖注入;上面分析过,这里不做过多分析

实例化所有的包装类,instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); ,可以看到遍历了所有的包装类,并且每实例化成功一个包装类并且镜像依赖注入的操作之后,这个新的实例就成为下一个包装类实例化时候的入参; 反正就是可以一层一层的往下包装下去;

将创建好的实例放到 cachedInstances 中

@Activate 注解的作用

TODO…

总结

自适应扩展机制

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂

Adaptive 可注解在类或方法上。当 Adaptive 注解在类上时,Dubbo 不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo 则会为该方法生成代理逻辑。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive 是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。Adaptive 注解的地方不同,相应的处理逻辑也是不同的。

对于要生成自适应拓展的接口,Dubbo 要求该接口至少有一个方法被 Adaptive 注解修饰。若不满足此条件,就会抛出运行时异常

一个方法可以被 Adaptive 注解修饰,也可以不被修饰。这里将未被 Adaptive 注解修饰的方法称为“无 Adaptive 注解方法,生成的方法是抛出异常 throw new UnsupportedOperationException

方法代理逻辑会从 URL 中提取目标拓展的名称,Dubbo 使用 URL 对象(包含了 Key-Value)传递配置信息。

扩展点方法调用会有 URL 参数(或是参数有 URL 成员)

这样依赖的扩展点也可以从 URL 拿到配置信息,所有的扩展点自己定好配置的 Key 后,配置信息从 URL 上从最外层传入。URL 在配置传递上即是一条总线

Adaptive 注解值 value 类型为 String[],可填写多个值,默认情况下为空数组。若 value 为非空数组,直接获取数组内容即可。若 value 为空数组,则需进行额外处理。处理过程是将类名转换为字符数组,然后遍历字符数组,并将字符放入 StringBuilder 中。若字符为大写字母,则向 StringBuilder 中添加点号,随后将字符变为小写存入 StringBuilder 中。比如 LoadBalance 经过处理后,得到 load.balance。

上面得到的值,例如 load.balance, 然后从 URL 中取获取对应需要自适应扩展的实现类名;用 url.getParameter(value,defaultvalue)得到 extName 需要要使用的扩展类;

最后(com.alibaba.dubbo.rpc.Protocol) ExtensionLoader .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName) 拿到了正真要使用的扩展类,然后用这个实例类去调用 被 @Adaptive 修饰的方法名;

例如 return extension.refer(arg0, arg1);

扩展点自动包装

自动包装扩展点的 Wrapper 类。ExtensionLoader 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。

Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类

扩展点的 Wrapper 类可以有多个,也可以根据需要新增。

通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。

扩展点自动装配

主要的方法就是 injectExtension;

Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。

objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。

Dubbo IOC 目前仅支持 setter 方式注入

扩展点自动激活

对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同时加载多个实现,此时,可以用自动激活来简化配置,如:

import org.apache.dubbo.common.extension.Activate;import org.apache.dubbo.rpc.Filter; @Activate // 无条件自动激活public class XxxFilter implements Filter {    // ...}
复制代码


import org.apache.dubbo.common.extension.Activate;import org.apache.dubbo.rpc.Filter; @Activate("xxx") // 当配置了xxx参数,并且参数为有效值时激活,比如配了cache="lru",自动激活CacheFilter。public class XxxFilter implements Filter {    // ...}
复制代码


import org.apache.dubbo.common.extension.Activate;import org.apache.dubbo.rpc.Filter; @Activate(group = "provider", value = "xxx") // 只对提供方激活,group可选"provider"或"consumer"public class XxxFilter implements Filter {    // ...}
复制代码


发布于: 2022-11-14阅读数: 29
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019-09-06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 20w字《Kafka运维与实战宝典》PDF下载请关注公众号:石臻臻的杂货铺

评论

发布
暂无评论
【Dubbo源码】SPI机制源码解析_dubbo_石臻臻的杂货铺_InfoQ写作社区