Dubbo源码篇07---SPI神秘的面纱---原理篇---下

Dubbo源码篇07---SPI神秘的面纱---原理篇---下

  • 引言
  • 根据name获取扩展实例对象
  • 获取默认扩展实例对象
  • 按条件批量获取扩展实例对象
    • 实例演示
  • 小结


引言

上篇文章: Dubbo源码篇06—SPI神秘的面纱—原理篇—上 我们追踪了getAdaptiveExtension获取自适应扩展点的整个流程,整个流程核心如下:

private T createAdaptiveExtension() {
        T instance = (T) getAdaptiveExtensionClass().newInstance();
        instance = postProcessBeforeInitialization(instance, null);
        injectExtension(instance);
        instance = postProcessAfterInitialization(instance, null);
        initExtension(instance);
        return instance;
}

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

因为自适应扩展点在dubbo中的用意是用来实现运行时动态选择实现类的,所以不会对自适应扩展点赋予AOP能力,从上面的流程中我们也没有发现哪里存在Wrapper机制的处理。

所以本文我们顺着普通扩展类加载流程,来过一遍dubbo对AOP的处理过程:

        ApplicationModel applicationModel = ApplicationModel.defaultModel();
        ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
        FrameWork frameWork = extensionLoader.getExtension("guice");

根据name获取扩展实例对象

根据传入的name作为serviceKey去加载对应的扩展实现:

    public T getExtension(String name) {
        //第二个参数表明是否对当前扩展类启动Wrapper装饰
        T extension = getExtension(name, true);
        if (extension == null) {
            throw new IllegalArgumentException("Not find extension: " + name);
        }
        return extension;
    }
public T getExtension(String name, boolean wrap) {
        ...
        //如果name为true,那么去获取默认扩展实现
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        String cacheKey = name;
        if (!wrap) {
            cacheKey += "_origin";
        }
        //查询缓存--没有新建一个Holder返回
        final Holder<Object> holder = getOrCreateHolder(cacheKey);
        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;
    }
private T createExtension(String name, boolean wrap) {
        //getExtensionClasses方法上篇文章解析过了,这里跳过
        Class<?> clazz = getExtensionClasses().get(name);
        //如果没有key=name的扩展实现,则抛出异常
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
           // 判断对应的扩展实现类型是否已经创建过了实例对象--确保单例性 
            T instance = (T) extensionInstances.get(clazz);
            //如果只是解析了SPI文件,构成了<name,class>缓存,下一步就是为当前扩展类型构建<class,signleInstance>缓存
            if (instance == null) {
                //利用instantiationStrategy实例化扩展实例对象---具体逻辑在InstantiationStrategy中
                //实例化逻辑比较简单: 要不就是默认构造,要么构造函数可以有参数,但是参数类型必须是ScopeModel子类
                extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
                instance = (T) extensionInstances.get(clazz);
                //前后置处理--依赖注入
                instance = postProcessBeforeInitialization(instance, name);
                injectExtension(instance);
                instance = postProcessAfterInitialization(instance, name);
            }
            //和自适应扩展点创建的不同逻辑: 判断是否需要对当前扩展实例进行装饰
            if (wrap) {
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                //当前扩展类相关wrapper类型搜集工作在getExtensionClasses中完成
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }
                
                //wrapper class搜集是满足存在一个单参数的拷贝构造函数,并且参数类型为当前扩展类类型
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    //不断循环,套娃创建一层层的装饰器对象
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        //Wrapper注解用于实现按条件装饰
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        //如果wrapper class类上不存在Wrapper注解,那么表示装饰不需要满足任何条件
                        //否则,需要判断条件是否满足,满足才会进行装饰
                        boolean match = (wrapper == null) ||
                            ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                                !ArrayUtils.contains(wrapper.mismatches(), name));
                        if (match) {
                            //满足则进入装饰流程
                            //1.实例化当前装饰类,采用的是单参的拷贝构造函数
                            //2.执行依赖注入流程
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                            //3.执行后置处理流程
                            instance = postProcessAfterInitialization(instance, name);
                        }
                    }
                }
            }

            // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
            //调用初始化接口---注意上面警告信息,也就是说经过包装后,我们的包装对象未必继承lifecycle接口,因此初始化调用也就不会发生了
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

获取默认扩展实例对象

采用@SPI注解中的val值,作为serviceKey去加载对应的扩展实现:

    public T getDefaultExtension() {
        //加载SPI文件,构建相关缓存,如: <name,class>
        getExtensionClasses();
        //cachedDefaultName来自@SPI注解中的val值
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        //这个流程上面讲过了
        return getExtension(cachedDefaultName);
    }

按条件批量获取扩展实例对象

到现在为止,我们还差extensionLoader.getActivateExtensions()流程没有讲解,下面我们来看看按条件批量获取扩展实例对象是怎样实现的:

    public List<T> getActivateExtension(URL url, String key, String group) {
        //根据传入的key从url中提取出value值
        String value = url.getParameter(key);
        //如果value不为空,则按照","分割,作为serviceKey
        return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
    }
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        checkDestroyed();
        // solve the bug of using @SPI's wrapper method to report a null pointer exception.
        Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(activateComparator);
        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
        Set<String> namesSet = new HashSet<>(names);
        if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            //集合条件缓存构建: <serviceKey,groups>  和 <serviceKey,keyParis>
            if (cachedActivateGroups.size() == 0) {
                synchronized (cachedActivateGroups) {
                    // cache all extensions
                    if (cachedActivateGroups.size() == 0) {
                        //加载当前扩展类对应的SPI资源文件,并建立好相关缓存映射,此处主要为cachedActivates映射
                        //cachedActivates缓存了<serviceKey,@Activate注解> (serviceKey就是我们在SPI文件: serviceKey=serivceImpl全类名)
                        //如果配置文件中没有指定serviceKey,那么为@Extension注解中指定的val值,如果没有注解,那么就为实现类的简单类名
                        getExtensionClasses();
                        //依次处理当前扩展类下所有标注了@Activate注解的实现类
                        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                            //key为serviceKey
                            String name = entry.getKey();
                            //activate注解
                            Object activate = entry.getValue();

                            String[] activateGroup, activateValue;
                            //提取注解中的值
                            if (activate instanceof Activate) {
                                activateGroup = ((Activate) activate).group();
                                activateValue = ((Activate) activate).value();
                            } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                                activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                                activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                            } else {
                                continue;
                            }
                            //缓存<serviceKey,groups>映射,即激活当前实现类,需要满足哪些分组要求(满足其一即可)
                            cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
                            //缓存<serviceKey,keyParis>映射
                            //activate注解中的value属性有两种写法: key1:val1 或者 key2
                            //前者表示URL中存在key1=val1的键值对才算满足条件
                            //后置表示URL中存在key2即满足条件
                            String[][] keyPairs = new String[activateValue.length][];
                            for (int i = 0; i < activateValue.length; i++) {
                                if (activateValue[i].contains(":")) {
                                    keyPairs[i] = new String[2];
                                    String[] arr = activateValue[i].split(":");
                                    keyPairs[i][0] = arr[0];
                                    keyPairs[i][1] = arr[1];
                                } else {
                                    keyPairs[i] = new String[1];
                                    keyPairs[i][0] = activateValue[i];
                                }
                            }
                            cachedActivateValues.put(name, keyPairs);
                        }
                    }
                }
            }

            // traverse all cached extensions
            //遍历<serviceKey,groups>映射
            cachedActivateGroups.forEach((name, activateGroup) -> {
                // 如果函数调用中传入的group匹配条件为空,或者group存在于groups集合,则满足分组匹配这个条件
                if (isMatchGroup(group, activateGroup)
                 //nameSet是从传入函数中的key,从url中获取value后,按照","分割,得到的集合
                 //这里去掉serviceKey=name的处理,因为该逻辑会在下面被处理   
                    && !namesSet.contains(name)
                    && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
                    //从<serviceKey,keyParis>集合中获取激活当前扩展实现类,需要满足哪些用户自定义条件
                    //这里判断逻辑就是: 如果用户指定的是形如@Active(value="key1:value1, key2:value2")
                    //那么会从url先中取出key1的值,与value1进行比较,相等直接返回true,否则取出key2值继续判断,也就是说这里是任意条件满足就返回true
                    //如果用户注解中只指定了@Active(value="key1"),那么只要url中存在key1,就满足条件
                    && isActive(cachedActivateValues.get(name), url)) {
                    
                    //如果分组条件和用户自定义条件都满足,则加入activateExtensionsMap集合<class,Instance>
                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                }
            });
        }

        if (namesSet.contains(DEFAULT_KEY)) {
           ...
        } else {
            // add extensions, will be sorted by its order
            for (int i = 0; i < names.size(); i++) {
                String name = names.get(i);
                if (!name.startsWith(REMOVE_VALUE_PREFIX)
                    && !namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                    if (!DEFAULT_KEY.equals(name)) {
                        //<serviceKey,class>集合中包含serviceKey=name
                        if (containsExtension(name)) {
                            //则将serviceKey=name的扩展实现类也加入结果集合
                            activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                        }
                    }
                }
            }
            //返回最终得到的集合扩展实现类集合
            return new ArrayList<>(activateExtensionsMap.values());
        }
    }

上面这一大段看下来可能会比较懵逼,但是没关系,下图详细解释了按照激活条件筛选的整个流程:
在这里插入图片描述

  • 如果某个serviceKey对应的keyParis为空,也就是说用户没有自定义匹配条件,那么该条件分支默认返回true。
  • 如果函数传入的group为空,那么不考虑分组匹配条件,该条件分支默认返回true

注意: 放入结果集合前,扩展类的获取调用的是getExtension方法,意味着按条件批量获取扩展实例对象场景下,实现类是享有AOP(Wrapper机制)支持的:

   activateExtensionsMap.put(getExtensionClass(name), getExtension(name));

实例演示

  • 扩展接口,及其实现类
@SPI("spring")
public interface FrameWork {
    @Adaptive
    String getName(URL url);
    String getInfo();
}

@Activate(value = {"name:dhy","age:18"},group = "test")
public class Guice implements FrameWork{
    @Override
    public String getName(URL url) {
        return "guice";
    }

    @Override
    public String getInfo() {
        return "google 开源的轻量级IOC框架";
    }
}

@Activate(value = {"name","sex"},group = "test")
public class Spring implements FrameWork{
    @Override
    public String getName(URL url) {
        return "spring";
    }

    @Override
    public String getInfo() {
        return "流行的Spring框架";
    }
}


@Activate(group = "prod")
public class SpringBoot implements FrameWork{
    @Override
    public String getName(URL url) {
        return "springBoot";
    }

    @Override
    public String getInfo() {
        return "自动化的SpringBoot框架";
    }
}
  • SPI文件内容
spring=com.adaptive.Spring
springBoot=com.adaptive.SpringBoot
guice=com.adaptive.Guice
  • 测试类
class ActivateTest {
    @Test
    void activateTest() {
        ApplicationModel applicationModel = ApplicationModel.defaultModel();
        ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
        List<FrameWork> frameWorkList = extensionLoader.getActivateExtension(URL.valueOf("dubbo://127.0.0.1:80/?age=18"), "", "test");
        frameWorkList.forEach(frameWork -> {
            System.out.println(frameWork.getInfo());
        });
    }
}

在这里插入图片描述
大家可自行更改参数条件,测试其他分支。


小结

本文主要带领大家过了一遍普通扩展类的加载流程,看到了普通扩展类加载与自适应扩展点加载的不同的之处,区别在于自适应扩展点没有Wrapper机制支持,这是因为自适应扩展点设计的本意是运行时根据条件动态选择扩展类。

@Acativate注解按运行时条件决定是否激活当前扩展实现类,并且可以激活满足条件的所有扩展实现类,所以是按条件批量激活。

按条件批量激活的扩展实现类只是在普通扩展类基础上加了一层按条件激活,因此满足条件时,最终获取按条件激活的扩展实现类也是调用的getExtension方法,可知也是享有dubbo提供的Wrapper机制支持的。

自适应扩展点是通过方法参数传入URL,在自适应扩展点内部根据条件判断选择哪个扩展实现。而按条件激活扩展,是在getActivateExtension方法内部进行的条件判断,这一点大家需要注意一下。

普通扩展类,自适应扩展点和按条件激活的扩展类加载,三种方式都享有依赖注入和前后置处理支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/24306.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(常见)数据模型

文章目录 数据模型概述一、数据模型概要1.模型、建模与抽象2.数据模型3.两类数据模型 二、数据库模型的组成要素1.数据结构2.数据操作3.数据的完整性约束 三、概念模型1.概要2.基本概念3.概念模型的表示方法 常用数据模型一、层次模型1.简介2.数据结构3.数据操纵与完整性约束4.…

【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例

UART 简介 我们在使用 PS 的时候&#xff0c;通常会添加 UART 控制器&#xff0c;用于打印信息和调试代码。除此之外&#xff0c;PS 在和外 部设备通信时&#xff0c;也会经常使用串口进行通信。 UART 控制器 UART 控制器是一个全双工异步收发控制器&#xff0c;ZYNQ 内部包…

教你一步步使用实现TensorFlow 进行对象检测

在本文中,我们将学习如何使用 TensorFlow Hub 预训练模型执行对象检测。TensorFlow Hub 是一个库和平台,旨在共享、发现和重用预训练的机器学习模型。TensorFlow Hub 的主要目标是简化重用现有模型的过程,从而促进协作、减少冗余工作并加速机器学习的研发。用户可以搜索社区…

Linux内核源码分析-进程调度(五)-组调度

出现的背景 总结来说是希望不同分组的任务在高负载下能分配可控比例的CPU资源。为什么会有这个需求呢&#xff0c;假设多用户计算机系统每个用户的所有任务划分到一个分组中&#xff0c;A用户90个任务&#xff0c;而B用户只有10个任务&#xff08;这100个任务假设都是优先级一…

Python 下载的 11 种姿势,一种比一种高级

今天我们一起学习如何使用不同的Python模块从web下载文件。此外&#xff0c;你将下载常规文件、web页面、Amazon S3和其他资源。 通过本文的学习&#xff0c;你将学到如何克服可能遇到的各种挑战&#xff0c;例如下载重定向的文件、下载大型文件、完成一个多线程下载以及其他策…

C# WPF窗体设计器显示以及App.xaml文件打不开(VS 2022)

问题描述&#xff1a; 在项目中遇到了App.xaml设计器打不开以及窗体设计器不显示&#xff0c;只有代码&#xff0c;如图所示&#xff1a; 可以明显的看见左下角的设计器不见&#xff0c;但是用户控件又有设计器 解决方法&#xff1a; (一、App.xaml不能正常打开) ①清理项…

定薪17K*15,阿里测开岗上岸面经分享....

先简单介绍一下我自己吧&#xff0c;等会大家以为我是什么学历狂人&#xff0c;技术大牛&#xff0c;我毕业于广东一个普通本科院校&#xff0c;绝对不是什么双一流大学&#xff0c;大家不要有距离感&#xff0c;这也是我为什么来分享的原因&#xff0c;因为我觉得我这段经验还…

硬件软件【部署】

开发板和主机 1.功能不同&#xff1a;帮助开发者进行嵌入式系统的开发和调试&#xff0c;具有较强的硬件拓展能力&#xff0c;可以连接各种传感器/执行器等外设。主机为满足一般的计算需求而设计&#xff0c;具备更强的计算和图形处理能力。 2.架构不同&#xff1a;开发板通常…

【接口测试】JMeter测试WebSocket接口

目录 一、WebSocket简介 二、JMeter测试WebSocket接口 三、WebSocket和Socket的区别 最近老被问到WebSocket&#xff0c;突然想到以前大学时上Java课的时候&#xff0c;老师教我们socket连接&#xff0c;一个同学电脑做客户端&#xff0c;一个同学电脑做服务端&#xff0c;…

LAMP平台搭建

文章目录 LAMP概述安装apache安装mysql安装php LAMP概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统、Apache网站…

Java --- 期末复习卷

一、单选题 1&#xff0e;所有Java应用程序主类必须有一个名叫( )的方法。[ ] A&#xff0e;method B&#xff0e;main() C&#xff0e;java() D&#xff0e;hello 2&#xff0e;编写并保存了一个Java程序文件之后&#xff0c;( )它。[ …

node笔记_http服务搭建(渲染html、json)

文章目录 ⭐前言⭐初始化项目调整npm 的script运行入口搭建hello world的http服务npm run dev执行主函数的http服务 ⭐http返回类型html模板文件返回安装express渲染html的字符串 渲染html文件 sendFile渲染json返回数据类型 res.json ⭐结束 ⭐前言 大家好&#xff0c;我是ym…

TensorFlow 1.x学习(系列二 :4):自实现线性回归

目录 线性回归基本介绍常用的op自实现线性回归预测tensorflow 变量作用域模型的保存和加载 线性回归基本介绍 线性回归&#xff1a; w 1 ∗ x 1 w 2 ∗ x 2 w 3 ∗ x 3 . . . w n ∗ x n b i a s w_1 * x_1 w_2 * x_2 w_3 * x_3 ... w_n * x_n bias w1​∗x1​w2​∗…

MySQL 对日期使用 DATE_FORMAT()函数

文章目录 DATE_FORMAT()函数显示今天是星期几只显示年月显示当前时间的分钟数和秒数 DATE_FORMAT()函数 前面使用日期时间函数&#xff0c;获取到的要么是 yyyy-mm-dd 形式的日期&#xff0c;要么是 hh:MM:ss 形式的时间&#xff0c;或者是 yyyy-mm-dd hh:mm:ss 形式的日期及时…

算法基础学习笔记——⑧堆\哈希表

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨堆 &#x1f353;堆模板&#xff1a; ✨哈希表 &#x1f353;一般哈希模板&#xff1a; &#x1f353;字符串哈希模板&#xff1a; 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(…

Qt文件系统源码分析—第六篇QSaveFile

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

远程访问本地jupyter notebook服务 - 无公网IP端口映射

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自远控源码文章&#xff1a;公网远程访问jupyter notebook【cpolar内网穿透】 前言 Jupyter Notebook&am…

网络原理(六):http 协议(上)

目录 HTTP 协议是什么 抓包工具 Fiddler 的下载 使用Fiddler HTTP 请求 (Request) HTTP 请求格式 首行 请求头&#xff08;Header&#xff09; Cookie HTTP 协议是什么 还是老样子&#xff0c;在讲解http 之前我们先来了解以下什么叫做 http 。 HTTP&#xff08;Hyp…

(转载)从0开始学matlab(第14天)—while循环结构

循环(loop) 是一种 matlab 结构&#xff0c;它允许我们多次执行一系列的语句。循环结构有两种基本形式 :while 循环和 for 循环。两者之间的最大不同在于代码的重复是如何控制的。在while 循环中&#xff0c;代码的重复的次数是不能确定的&#xff0c;只要满足用户定义的条件…

整理6个超好用的在线编辑器!

随着 Web 开发对图像可扩展性、响应性、交互性和可编程性的需求增加&#xff0c;SVG 图形成为最适合 Web 开发的图像格式之一。它因文件小、可压缩性强并且无论如何放大或缩小&#xff0c;图像都不会失真而受到欢迎。然而&#xff0c;为了编辑 SVG 图像&#xff0c;需要使用 SV…