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

简介: 【Dubbo源码】SPI机制源码解析

文章目录

什么是SPI机制

SPI示例

Java SPI示例

Dubbo SPI 示例

Dubbo SPI机制源码分析

ExtensionLoader静态类和静态方法

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

如何创建自适应扩展实例

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

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

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

依赖注入的属性从哪里来

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

加载文件中的具体实现类

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

@Activate 注解的作用

总结

自适应扩展机制

扩展点自动包装

扩展点自动装配

扩展点自动激活

参考文章

什么是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 的源码。


SPI示例

Java 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.OptimusPrime
org.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

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


Dubbo SPI 示例

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

optimusPrime = org.apache.spi.OptimusPrime
bumblebee = 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

Dubbo SPI机制源码分析

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


ExtensionLoader静态类和静态方法

如果你有留心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<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS这个Map是用来存放所有的SPI的管理的Class和ExtensionLoader扩展器的;

比如dubbo内置的


image.png

image.png

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


ConcurrentMap<Class<?>, 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<T>(type) ;

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

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


image.png

image.png

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

以首先被加载的Protocol为例

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

最终是执行了new ExtensionLoader<T>(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

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

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

如何创建自适应扩展实例

    @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依赖注入;

那么问题又来了

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

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


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

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

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

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

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

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

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

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

    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

拼接完成这个类之后,然后选择某个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权限的,就可以依赖注入了

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

依赖注入的属性从哪里来

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

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

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

dubbo中的自适应扩展类是AdaptiveExtensionFactory

@Adaptive
public 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

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

例如SpiExtensionFactory;

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

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

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

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

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

加载文件中的具体实现类

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

如果不是自适应扩展类也不是包装类,并且扩展类带有注解@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 {
    // ...
}


目录
相关文章
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
501 2
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
575 3
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
13637 46
|
PHP 开发者 UED
PHP中的异常处理机制解析####
本文深入探讨了PHP中的异常处理机制,通过实例解析try-catch语句的用法,并对比传统错误处理方式,揭示其在提升代码健壮性与可维护性方面的优势。文章还简要介绍了自定义异常类的创建及其应用场景,为开发者提供实用的技术参考。 ####
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
缓存 NoSQL Java
千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析
【11月更文挑战第30天】在千万级电商系统中,ID生成机制是核心基础设施之一。一个高效、可靠的ID生成系统对于保障系统的稳定性和性能至关重要。本文将深入探讨一种在千万级电商线上广泛应用的ID生成机制——无阻塞双buffer缓冲优化方案。本文从概述、功能点、背景、业务点、底层原理等多个维度进行解析,并通过Java语言实现多个示例,指出各自实践的优缺点。希望给需要的同学提供一些参考。
281 8
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
660 8
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
543 5

推荐镜像

更多
  • DNS
  • 下一篇
    开通oss服务