Dubbo3 源码解读-宋小生-6:Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: > Dubbo3 已经全面取代 HSF2 成为阿里的下一代服务框架,2022 双十一基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,极大的推动了开源技术与标准的发展。> 本文是
Dubbo3 已经全面取代 HSF2 成为阿里的下一代服务框架,2022 双十一基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,极大的推动了开源技术与标准的发展。

本文是 Dubbo 社区贡献者宋小生基于 Dubbo3 3.0.8 版本撰写的源码解析博客,在 Dubbo3 开源&内部技术栈统一的情况下,期望能对集团内的开发者了解 Dubbo3 背后的实现原理有所帮助。可点此查看 博客原文

本篇是宋小生系列 6/30 篇。同时,由 Dubbo3 团队领导的源码解读系列也正在进行中,感兴趣的同学可加入钉钉群了解详情: 28165003194

6.1 普通扩展对象的加载与创建

这里我们要分析的是ExtensionLoader类型的getExtension(String name)方法, 有了前面自适应扩展的铺垫,这里就更容易来看了getExtension是根据扩展名字获取具体扩展的通用方法,我们来根据某个类型来获取扩展的时候就是走的这里,比如在这个博客开头的介绍:

  • ApplicationModel中获取配置管理器对象
 configManager = (ConfigManager) this.getExtensionLoader(ApplicationExt.class)
                .getExtension(ConfigManager.NAME);
       

6.1.1 getExtension方法源码

先来看下getExtension方法的源码,根据扩展名字查询扩展对象


public T getExtension(String name) {
        //这里并不能看到什么,只多传了个参数wrap为true调用另外一个重载的方法
        T extension = getExtension(name, true);
        if (extension == null) {
            throw new IllegalArgumentException("Not find extension: " + name);
        }
        return extension;
    }
public T getExtension(String name, boolean wrap) {
        //检查扩展加载器是否已被销毁
        checkDestroyed();
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        //扩展名字为true则加载默认扩展
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //非wrap类型则将缓存的扩展名字key加上_origin后缀
        //wrap是aop机制 俗称切面,这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的
        String cacheKey = name;
        if (!wrap) {
            cacheKey += "_origin";
        }
        //从cachedInstances缓存中查询
        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;
    }

我们先来看一下默认扩展的加载代码:

public T getDefaultExtension() {
        //加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型,将对象缓存在cachedDefaultName中
        getExtensionClasses();
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        //再回到加载扩展的方法
        return getExtension(cachedDefaultName);
    }

创建扩展对象方法这个和自适应扩展的创建扩展类似
createExtension:
具体过程如下:

  • 加载扩展类型:getExtensionClasses()
  • 创建扩展对象:createExtensionInstance(clazz)
  • 注入自适应扩展: injectExtension(instance);
  • wrap处理
private T createExtension(String name, boolean wrap) {
        //扩展的创建的第一步扫描所有jar中的扩展实现,这里扫描完之后获取对应扩展名字的扩展实现类型的Class对象
        Class<?> clazz = getExtensionClasses().get(name);
        //出现异常了 转换下异常信息 再抛出
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            //当前扩展对象是否已经创建过了则直接从缓存中获取
            T instance = (T) extensionInstances.get(clazz);
            if (instance == null) {
                //第一次获取缓存中肯定没有则创建扩展对象然后缓存起来
                //createExtensionInstance 这个是与自适应扩展对象创建对象的不同之处
                extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
                instance = (T) extensionInstances.get(clazz);
                instance = postProcessBeforeInitialization(instance, name);
                //注入扩展自适应方法,这个方法前面讲自适应扩展时候说了,注入自适应扩展方法的自适应扩展对象
                injectExtension(instance);
                instance = postProcessAfterInitialization(instance, name);
            }
            //是否开启了wrap
            //Dubbo通过Wrapper实现AOP的方法
            if (wrap) {
            //这个可以参考下Dubbo扩展的加载
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                //wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    //根据Wrapper注解的order值来进行排序值越小越在列表的前面
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    //反转之后值越大就会在列表的前面
                    Collections.reverse(wrapperClassesList);
                }
                //从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        //需要包装的扩展名。当此数组为空时,默认值为匹配
                        //看下当前扩展是否匹配这个wrap,如何判断呢?
                        //wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展
                        //如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装
                        boolean match = (wrapper == null) ||
                            ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                                !ArrayUtils.contains(wrapper.mismatches(), name));
                                //这是扩展类型是匹配wrapp的则开始注入
                        if (match) {
                        //匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                            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);
        }
    }

6.1.2 创建扩展对象

前面加载扩展类型在自适应扩展的时候已经说过了这里就不重复了,这里我们来看下
扩展对象的创建过程:createExtensionInstance(clazz)

前面看自适应扩展对象创建的时候自适应扩展对象仅仅是使用反射newInstance了一个扩展对象,而普通的扩展类型创建对象的过程就相对复杂一点,接下来我们来看下:

ExtensionLoader的createExtensionInstance方法

private Object createExtensionInstance(Class<?> type) throws ReflectiveOperationException {
        //在ExtensionLoader构造器中,有个initInstantiationStrategy()方法中new了一个初始化策略InstantiationStrategy类型对象 
        return instantiationStrategy.instantiate(type);
    }

InstantiationStrategy的实例化对象方法instantiate

public <T> T instantiate(Class<T> type) throws ReflectiveOperationException {

        // should not use default constructor directly, maybe also has another constructor matched scope model arguments
        // 1. try to get default constructor
        Constructor<T> defaultConstructor = null;
        try {
            //反射获取对应类型的无参构造器
            defaultConstructor = type.getConstructor();
        } catch (NoSuchMethodException e) {
            // ignore no default constructor
        }

        // 2. use matched constructor if found
        List<Constructor> matchedConstructors = new ArrayList<>();
        //获取所有构造器
        Constructor<?>[] declaredConstructors = type.getConstructors();
        //遍历构造器列表,
        for (Constructor<?> constructor : declaredConstructors) {
        //如果存在构造器则构造器参数类型是否为ScopeModel类型,如果为ScopeModel则为匹配的构造器 说明我们扩展类型在这个版本如果想要让这个构造器生效必须参数类型为ScopeModel
            if (isMatched(constructor)) {
                matchedConstructors.add(constructor);
            }
        }
        // remove default constructor from matchedConstructors
        if (defaultConstructor != null) {
            matchedConstructors.remove(defaultConstructor);
        }

        // match order:
        // 1. the only matched constructor with parameters
        // 2. default constructor if absent
        
        Constructor targetConstructor;
        //匹配的参数ScopeModel的构造器太多了就抛出异常
        if (matchedConstructors.size() > 1) {
            throw new IllegalArgumentException("Expect only one but found " +
                matchedConstructors.size() + " matched constructors for type: " + type.getName() +
                ", matched constructors: " + matchedConstructors);
        } else if (matchedConstructors.size() == 1) {
        //一个参数一般为一个参数类型ScopeModel的构造器
            targetConstructor = matchedConstructors.get(0);
        } else if (defaultConstructor != null) {
        //如果没有自定义构造器则使用空参数构造器
            targetConstructor = defaultConstructor;
        } else {
        //一个构造器也没匹配上也要报错
            throw new IllegalArgumentException("None matched constructor was found for type: " + type.getName());
        }

        // create instance with arguments
        //反射获取构造器参数的参数类型列表
        Class[] parameterTypes = targetConstructor.getParameterTypes();
        //如果存在参数则为参数设置值
        Object[] args = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
        //借助scopeModelAccessor工具获取参数类型,这个参数类型为当前的域模型对象
            args[i] = getArgumentValueForType(parameterTypes[i]);
        }
        //创建扩展对象
        return (T) targetConstructor.newInstance(args);
    }

6.2 wrap机制

6.2.1 Wrapper机制说明

Dubbo通过Wrapper实现AOP的方法

Wrapper机制,即扩展点自动包装。Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个,也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。

Wrapper的规范
Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。
Wrapper 类在定义时需要遵循如下规范。

  • 该类要实现 SPI 接口
  • 该类中要有 SPI 接口的引用
  • 该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI接口
  • 在接口实现方法中要调用 SPI 接口引用对象的相应方法
  • 该类名称以 Wrapper 结尾

比如如下几个扩展类型

 class org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
 class org.apache.dubbo.qos.protocol.QosProtocolWrapper
 class org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
 class org.apache.dubbo.qos.protocol.QosProtocolWrapper

回顾下Wrapper扩展类型的扫描于对象的创建

6.2.2 Wrapper类型的扫描

Wrapper类型的扫描代码如下:

来自4.5.2.3小节ExtensionLoader类型中的loadClass方法

 //扩展子类型是否存在这个注解@Adaptive
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {
        //扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型)
            cacheWrapperClass(clazz);
        } else {

isWrapperClass方法通过判断构造器类型是否为当前类型来判断是否为Wrapper类型

 private boolean isWrapperClass(Class<?> clazz) {
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

6.2.3 Wrapper类型的创建

这个可以看下4.6.1 getExtension方法源码的获取扩展对象时候查询扩展对象是否有对应的Wrapper类型的扩展为其创建Wrapper扩展对象,如下代码

//Dubbo通过Wrapper实现AOP的方法
            if (wrap) {
            //这个可以参考下Dubbo扩展的加载
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                //wrap类型排序 这个wrap类型是如何来的呢,在前面扫描扩展类型的时候如果当前扩展类型不是Adaptive注解修饰的,并且当前类型type有个构造器参数是type自身的也是前面加载扩展类型时候说的装饰器模式 可以参考DubboProtocol的构造器
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    //根据Wrapper注解的order值来进行排序值越小越在列表的前面
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    //反转之后值越大就会在列表的前面
                    Collections.reverse(wrapperClassesList);
                }
                //从缓存中查到了wrapper扩展则遍历这些wrapp扩展进行筛选
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        //需要包装的扩展名。当此数组为空时,默认值为匹配
                        //看下当前扩展是否匹配这个wrap,如何判断呢?
                        //wrapper注解不存在或者matches匹配,或者mismatches不包含当前扩展
                        //如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装
                        boolean match = (wrapper == null) ||
                            ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                                !ArrayUtils.contains(wrapper.mismatches(), name));
                                //这是扩展类型是匹配wrapp的则开始注入
                        if (match) {
                        //匹配到了就创建所有的wrapper类型的对象同时构造器参数设置为当前类型
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                            instance = postProcessAfterInitialization(instance, name);
                        }
                    }
                }
            }

主要来看下什么情况下才为当前扩展类型创建Wrapper包装类型:

  • wrapper注解不存在(前面判断过Wrapper类型是构造器满足条件的)
  • 存在Wrapper注解:

    • matches匹配,
    • 或者mismatches不包含当前扩展

如果匹配到了当前扩展对象是需要进行wrapp的就为当前扩展创建当前wrapper扩展对象进行包装

目录
相关文章
|
20天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
20天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
20天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
3月前
【通信协议讲解】单片机基础重点通信协议解析与总结之SPI(二)
【通信协议讲解】单片机基础重点通信协议解析与总结之SPI(二)
|
4月前
|
缓存 负载均衡 Dubbo
Dubbo技术深度解析及其在Java中的实战应用
Dubbo是一款由阿里巴巴开源的高性能、轻量级的Java分布式服务框架,它致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
110 6
|
4月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
249 3
|
4月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
4月前
crash扩展 —— trace解析
crash扩展 —— trace解析
|
8月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
3月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
86 2

推荐镜像

更多