Dubbo源码剖析-SPI机制(超详细深度剖析篇)

目录

什么是SPI

SPI 的工作原理

SPI 的作用

SPI 的缺点

简单使用JDK的SPI与Dubbo的SPI

Dubbo为什么要使用SPI机制

Dubbo SPI 源码分析

小结


什么是SPI

        SPI 全称为 Service Provider Interface,一种解耦接口和实现的手段,其实现原理是将接口的实现类全名称配置在配置文件中,程序运行阶段去读取配置文件加载实现类,这个机制为程序带来 了很强的扩展性,使得我们可以很方便的 基于某接口规范去使用任何第三方的实现。

        SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中SPI 是一个非常重要的模块。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。下面,我们先来了解一下 Java SPI 与 Dubbo SPI 的使用方法,然后再来分析 Dubbo SPI 的源码。

SPI 的工作原理

  1. 定义服务接口:首先定义一个服务接口,通常这个接口会包含在 JAR 文件中。

  2. 提供服务实现:开发者可以通过创建该接口的具体实现类来提供服务。实现类的信息会被写入到 META-INF/services 目录下的一个文件中,文件名就是服务接口的全限定名,而文件内容则是实现类的全限定名。

  3. 查找服务实现:当需要使用该服务时,可以通过 ServiceLoader 类来加载所有可用的服务实现。ServiceLoader 会在类路径下搜索 META-INF/services 目录中的配置文件,并加载其中列出的服务实现。

SPI 的作用

  • 插件化:SPI 提供了一种在运行时动态发现并加载服务实现的方法,这使得系统可以在不知道具体实现的情况下使用各种不同的插件。

  • 扩展性:通过 SPI 可以轻松地添加新的服务实现而不必修改现有的代码。

  • 灵活性:SPI 允许在部署时选择不同的服务实现,这样可以根据环境的不同选择最合适的实现。

SPI 的缺点

  • 性能开销:每次启动应用时,SPI 都会扫描 META-INF/services 目录下的配置文件,如果有很多服务需要加载,这可能会导致较大的性能开销。

  • 初始化延迟:由于 SPI 是在运行时加载服务实现的,所以如果服务实现比较复杂或者有很多实现类,那么第一次使用服务可能会有明显的延迟。

  • 兼容性问题:如果多个服务提供者同时存在,可能会导致命名冲突或其他兼容性问题。

  • 安全性考虑:SPI 加载的类来自外部,如果没有适当的沙箱机制或安全检查,可能会引入安全风险。

尽管 SPI 存在一些缺点,但它仍然是 Java 中非常有用的设计模式,特别是在需要实现插件化和模块化系统时。开发者应该权衡其优缺点,并在适当的情景下使用它。

简单使用JDK的SPI与Dubbo的SPI

http://t.csdnimg.cn/JbqbB

Dubbo为什么要使用SPI机制

        dubbo作为一个rpc框架,在它发送RPC请求时,整个过程会经历很多个关键事件节点,比如集群容错,负载均衡,数据序列化,通信协议编码,网络传输等,每个关键节点都有抽象出对应的接口,而且有多种不同的实现,且用户可自行扩展,那实际运行阶段 dubbo如何根据用户的配置参数来选择具体的实现呢,这就促使dubbo需要一种可插拔的接口实现发现机制。

        dubbo采用微内核架构,将每一个功能接口当作一个可插拔的扩展点接口,内核层面只负责按流程组装并引导执行每个扩展点接口 ,具体的功能和逻辑由具体的扩展点实现来完成,提高了系统的扩展性和灵活性,而SPI就是实现微内核的手段。

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

Dubbo SPI 源码分析

上面我们简单演示了 Dubbo SPI 的使用方法。接下来我们来分析其源码

dubbo版本:2.7.19-relesse

    // 每个SPI接口都对应一个ExtensionLoader实例
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);


    @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 an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        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 的 getExtensionLoader 方法获取一个 ExtensionLoader 实例,然后再通过 ExtensionLoader 的 getExtension方法获取扩展点对象。这其中getExtensionLoader 用于从EXTENSION_LOADERS全局缓存中获取与扩展点对应的 ExtensionLoader扩展点执行器,若缓存未命中,则创建一个新的实例

    // 缓存 当前接口下所有完整的扩展点实例
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        return getExtension(name, true);
    }


    // 获取或创建扩展点实例
    private Holder<Object> getOrCreateHolder(String name) {
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        return holder;
    }

    // 获取扩展点
    public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 扩展点都是单实例的
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

拿到扩展点执行器后调用ExtensionLoader 的 getExtension 方法,此方法有两个参数name为扩展点名称,wrap标识是否进行Wrapper增强(Wrapper是SPI的一个高级特性,后续章节会讲到)

  1. 判断扩展点名称是否为true如果是则返回默认的扩展点
  2. 如果不为true,则获取全局缓存cachedInstances中的扩展点实例
  3. 接下来就是采用了单例模式的双重检查锁机制来检查缓存,缓存未命中则创建扩展点

下面我们来看一下创建扩展点对象的过程是怎样的。

/**
     * 创建扩展点
     * 1、完成文件解析,加载,
     * 2、完成基础实例的创建
     * 3、完成实例的注入
     * 4、完成实例的wrapper包装
     */
    @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        /**
         * getExtensionClasses方法很重要:
         *  完成了配置文件解析及加载,筛选
         */
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        ......此处省略下面会讲到
    }

创建扩展点对象的过程:

  1. 通过 getExtensionClasses方法获取所有的扩展点
  2. 通过反射创建扩展点对象
  3. 向扩展点对象实例中注入依赖
  4. 将扩展点包裹在相应的 Wrapper 对象中

以上步骤中,第一个步骤是加载扩展点的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现。在接下来的讲解中,我将会重点分析 getExtensionClasses 方法的逻辑,以及简单分析 Dubbo IOC 的具体实现。

    //缓存解析完的当前接口所有扩展点的 Class,key是扩展点名称
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

    /**
     * 完成 当前SPI扩展点接口的加载,解析,筛选
     * @return
     */
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses();// 只加载解析1次
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

我们在通过名称获取扩展点之前,首先需要根据配置文件加载解析出名称到扩展点的映射,也就是 Map<名称, 扩展点>。之后再从 Map 中筛选出响应的扩展点即可。这里也是先检查缓存,若缓存未命中,则采用了单例模式的双重检查锁机制来检查缓存,则加载扩展点。前面所分析的 getExtension 方法中有相似的代码。下面分析 loadExtensionClasses 方法的逻辑。

    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
    
    private static LoadingStrategy[] loadLoadingStrategies() {
        // 使用  JDK SPI 完成 LoadingStrategy的读取,可以在 dubbo-common模块中找到对应的配置
        return stream(load(LoadingStrategy.class).spliterator(), false)
                .sorted()
                .toArray(LoadingStrategy[]::new);
    }
   

    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();// 缓存默认扩展点名称

        Map<String, Class<?>> extensionClasses = new HashMap<>();

        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(),
                    strategy.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"),
                    strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

        return extensionClasses;
    }


    // 缓存默认扩展点名称
    private void cacheDefaultExtensionName() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }

        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];
            }
        }
    }

    

调用loadExtensionClasses 方法

  1. 缓存默认扩展点名称
  2. 之后就是根据约定的好的指定目录下读取配置文件
  3. 调用loadLoadingStrategies内部使用了JDK SPI来完成 LoadingStrategy的读取,可以在 dubbo-common模块中找到对应的配置
  4. 调用 loadDirectory 方法加载指定文件夹配置文件

下面我们来看一下 loadDirectory 做了哪些事情

    // 完成加载解析及筛选
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            ClassLoader classLoader = findClassLoader();

            // try to load from ExtensionLoader's ClassLoader first
            if (extensionLoaderClassLoaderFirst) {
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }

            if (urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }
            // 读取到的文件的完整路径
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

该方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源,接下来我们看一下 loadResource 方法的实现。

    // 完成加载解析与区分
    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                              java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                String clazz = null;
                // 按行读取
                while ((line = reader.readLine()) != null) {
                    // 去掉注释
                    final int ci = line.indexOf('#');
                    if (ci >= 0) {
                        line = line.substring(0, ci);
                    }
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
                            // 按=拆分 截取键与值。比如 dubbo=com.alibaba....DubboProtocol
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();// name
                                clazz = line.substring(i + 1).trim();// com.example.spi.Windows
                            } else {
                                clazz = line;
                            }
                            if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
                                // 完成类加载
                                loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException(
                                    "Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL +
                                            ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }

loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作。该方法的逻辑如下。

    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");    

    // 缓存用户自定义的自适应实例Class    
    private volatile Class<?> cachedAdaptiveClass = null;

    // 缓存所有的wrapper class
    private Set<Class<?>> cachedWrapperClasses;

    // 缓存 默认激活的 Activate信息,key是扩展点名称,value是标注的Activate注解
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();

    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();

    // 对加载到的 Class 进行筛选区分
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                           boolean overridden) throws NoSuchMethodException {
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }
        // 类上有 Adaptive 注解 ,则缓存
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {// 如果是wrapper 则缓存
            cacheWrapperClass(clazz);
        } else {
            clazz.getConstructor();// 确保有无参构造
            if (StringUtils.isEmpty(name)) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException(
                            "No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 缓存 默认激活的 Class
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    // 将 clazz 存入 extensionClasses 中
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }

loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass(自适应缓存)、cachedWrapperClasses(自动包装缓存)、cachedActivates (扩展点缓存)等等

  1. 1如果类上有 Adaptive 注解 ,则写入cachedAdaptiveClass缓存
  2. 判断是否写入cachedWrapperClasses缓存,判断依据则是看是否有接口类型的构造
  3. 最后将将 clazz 存入 extensionClasses中

到此,关于缓存类加载的过程就分析完了,接下来我们一直返回到createExtension方法,继续下面的逻辑。

    // 缓存扩展点的原始实例对象【原始实例:未wrapper,未inject】
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
    
    /**
     * 创建扩展点
     * 1、完成文件解析,加载,
     * 2、完成基础实例的创建
     * 3、完成实例的注入
     * 4、完成实例的wrapper包装
     */
    @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        /**
         * getExtensionClasses方法很重要:
         *  完成了配置文件解析及加载,筛选
         */
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            // 创建并保存扩展点原始实例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 完成扩展点实例的 inject 操作
            injectExtension(instance);

            // 完成对扩展点实例的包装
            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);// wrapper 排序
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        // wrapper 类上可以用 Wrapper 注解来标注 当前wrapper 是否对某扩展点进行增强
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            // wrapper 有接口类型的构造
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

            initExtension(instance);
            return instance;// 返回最终的扩展点实例
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }


回到createExtension方法完成了配置文件解析及加载,筛选之后,

  1. 创建并保存扩展点原始实例
  2. 完成扩展点实例的 inject 操作,对原始实例进行依赖注入
  3. 完成对扩展点实例的Wrapper包装

下面我们在讲下injectExtension方法



    private final ExtensionFactory objectFactory;

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


    private T injectExtension(T instance) {

        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {// 仅支持set注入
                    continue;
                }

                /*
                 * Check {@link DisableInject} to see if we need autowire injection for this property
                 */
                // set方法上标注 DisableInject 则放弃注入
                if (method.getAnnotation(DisableInject.class) != null) { 
                    continue;
                }
                // 拿到 set方法的参数类型
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                /*
                 * Check {@link Inject} to see if we need auto-injection for this property
                 * {@link Inject#enable} == false will skip inject property phase
                 * {@link Inject#InjectType#ByName} default inject by name
                 */
                String property = getSetterProperty(method);// 获取set方法对应的属性名称
                // set方法上可以标注 Inject ,指定注入的方式
                Inject inject = method.getAnnotation(Inject.class);
                if (inject == null) {
                    injectValue(instance, method, pt, property);
                } else {
                    if (!inject.enable()) {
                        continue;
                    }

                    if (inject.type() == Inject.InjectType.ByType) {
                        injectValue(instance, method, pt, null);
                    } else {
                        injectValue(instance, method, pt, property);
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }


    private String getSetterProperty(Method method) {
        return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
    }


    public static boolean isPrimitive(Class<?> cls) {
        return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
                || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
    }




    /**
     * 完成set注入
     * @param instance  当前实例对象
     * @param method    setXxx方法对应的method
     * @param pt        要注入的类型
     * @param property  对应的属性名称
     */
    private void injectValue(T instance, Method method, Class<?> pt, String property) {
        try {
            Object object = objectFactory.getExtension(pt, property);
            if (object != null) {
                method.invoke(instance, object);
            }
        } catch (Exception e) {
            logger.error("Failed to inject via method " + method.getName()
                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
        }
    }
  1. 判断是否支持set注入,不支持则continue
  2. 如果set方法有标注@DisableInject注解,则continue
  3. 拿到 set方法的参数类型,如果是原始类型,则continue
  4. 调用injectValue方法,根据类型与属性名称参数通过调用objectFactory的getExtension获取扩展点对象
  5. ExtensionFactory的内部也是通过SPI进行实现的,切断通过ExtensionLoader构造器可以发现,ExtensionFactory最后是获取的自适应的实例,而且ExtensionFactory还支持的从不同的扩展点工厂获取实例,包括SPI、Adaptive自适应、Spring

之后继续来到主干分支看下SPI的高级特性之一Wrapper是如何进行包装增强的
// 完成对扩展点实例的包装
            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);// wrapper 排序
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        // wrapper 类上可以用 Wrapper 注解来标注 当前wrapper 是否对某扩展点进行增强
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            // wrapper 有接口类型的构造
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

            initExtension(instance);

先判断全局缓存中的cachedWrapperClasses是否为空(在loadClass的时候已经将所有的Wrapper Class缓存起来了)

如果不为空时,会对Wrapper进行排序,排序规则会先识别@Activate注解,这个注解中会有order属性来新增包装顺序的控制,通常,数值越小优先级越高。如果没用@Activate注解,则会根据配置文件中的配置先后顺便来包装,配置越靠后,Wrapper则越先执行,如图:

接下来就是遍历所有的wrapper class,并识别有没有标注了@Wrapper注解,Wrapper默认是对所有扩展点进行包装,而@Wrapper注解中支持了对某个扩展点的包装或排除的控制,如图:

matches参数:代表需要包装的SPI扩展点,内部填入的是SPI扩展点名称

mismatches参数:代表要排除的SPI扩展点

到这里extensionLoader.getExtension的整个调用链就已经分析完成了。

小结

        本篇文章介绍了SPI 并对Dubbo的SPI源码进行了分析。在 Dubbo SPI 中还有一块重要的逻辑没有进行分析,那就是 Dubbo SPI 的扩展点自适应机制。该机制的逻辑较为复杂,我将会在下一篇文章中进行分析。

        最后送大家一句话,不要觉得源码很难从而恐惧阅读源码,要有着求贤若渴的心态,享受学习的过程学习源码中的设计思路及高级的写法,想象在实际业务中有没有可能用到,享受学习之后的成就感。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值