前言

在Dubbo SPI中是通过Wrapper实现AOP,对于AOP相信大家都不陌生,这里不做的过多的介绍,我们主要来了解Dubbo SPI中是如何使用Wrapper类以及实现的细节。

使用场景

Dubbo 中的一个扩展接口可以有多个扩展实现类,这些扩展实现类可能会包含一些相同的逻辑,如果在每个实现类中都写一遍,那么这些重复代码就会变得很难维护。因此Dubbo提供的自动包装特性(Wrapper),来解决这个问题。 Dubbo将多个扩展实现类的公共逻辑,抽象到Wrapper类中,同时Wrapper也实现了扩展接口,在获取真正的扩展实现对象时,相当于在其外面包装一层Wrapper对象,可以理解成一层装饰器,通过这样就可以在方法执行前后调用公共方法,也是一种AOP的体现。

举个栗子

  1. 定义接口;
@SPI
public interface WrapperDemo {
    void test();
}
  1. 定义接口实现;
public class WrapperDemoImpl implements WrapperDemo{
    @Override
    public void test() {
        System.out.println("WrapperDemoImpl test 方法执行");
    }
}
  1. 创建计算耗时包装类以及Order包装类;
public class CalWasteTimeWrapper implements WrapperDemo {

    private WrapperDemo wrapperDemo;

    public CalWasteTimeWrapper(WrapperDemo wrapperDemo) {
        this.wrapperDemo = wrapperDemo;
    }

    @Override
    public void test() {
        System.out.println("执行cal wrapper");
        System.out.println("执行统计耗时开始");
        long startTime = System.currentTimeMillis();
        wrapperDemo.test();
        System.out.println("执行耗时" + (System.currentTimeMillis() - startTime));
    }
}

public class OrderWrapper implements WrapperDemo {

    private WrapperDemo wrapperDemo;

    public OrderWrapper(WrapperDemo wrapperDemo) {
        this.wrapperDemo = wrapperDemo;
    }

    @Override
    public void test() {
        System.out.println("执行order wrapper");
        wrapperDemo.test();
    }
}
  1. 定义配置文件;
wrapperDemo=org.dubbo.spi.example.wrapper.WrapperDemoImpl
calWasteTimeWrapper=org.dubbo.spi.example.wrapper.CalWasteTimeWrapper
orderWrapper=org.dubbo.spi.example.wrapper.OrderWrapper
  1. 测试,这里我们会发现wrapper的顺序,越往下的越先被调用;
public class Test {
    public static void main(String[] args) {
        WrapperDemo wrapperDemo = ExtensionLoader
                .getExtensionLoader(WrapperDemo.class)
                .getExtension("wrapperDemo");
        wrapperDemo.test();
    }
}

image.png

源码

关于源码部分我们开始介绍时候已经带出来过关于包装类相关的介绍,这里我们主要挑重点代码进行介绍,核心可以分为两步,一步是加载时候,另外执行代码创建时候;

加载

加载就是在loadClass方法时候扩展类加载,判断是否是包装类进行加载,将所有的包装类的信息加载到ConcurrentHashSet中,对于包装类的判断也很简单,就是判断该类里面是否有type类型的构造参数,如果有就是包装类,否则会抛出异常, 返回false;

    //包装类判断
    else if (isWrapperClass(clazz)) {
       //缓存包装类
       cacheWrapperClass(clazz);
    }

    //使用ConcurrentHashSet缓存
    private void cacheWrapperClass(Class<?> clazz) {
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

    //判断是否是包装类
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

创建

创建部分是在createExtension方法中,对包装类进行创建,对于instance最后生成是WrapperDemo类型对象,里面包含OrderWrapper和CalWasteTimeWrapper对象,所以在最后调用的时候都进行输出,于是乎就有了Aop的效果;

    @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        //从配置文件中加载所有扩展类,形成配置项名称与配置类的映射关系
        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);
            }
            //向对象中注入依赖的属性 IOC自动装配
            injectExtension(instance);

            if (wrap) {
                //创建Wrapper扩展对象
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            //将当前instance作为参数创建wrapper实例,然后向wrapper实例中注入属性值
                            //并将wrapper实例赋值给instance
                            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);
        }
    }

结束

欢迎大家点点关注,点点赞!

Dubbo SPI-Wrapper的更多相关文章

  1. dubbo SPI设计

    SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时, ...

  2. 【Dubbo源码阅读系列】之 Dubbo SPI 机制

    最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...

  3. 搞懂Dubbo SPI可拓展机制

    前言 阅读本文需要具备java spi的基础,本文不讲java spi,please google it. 一.Dubbo SPI 简介 SPI(Service Provider Interface) ...

  4. Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI

    SPI 全称为 Service Provider Interface,是一种服务发现机制.当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类.所以在程序中并没有直接指定使用接口的哪个实 ...

  5. Dubbo——SPI及自适应扩展原理

    文章目录 引言 正文 一.什么是SPI? 1. Java SPI的实现 2. Dubbo SPI实现原理 由配置文件得到的猜想 SPI源码 二.自适应扩展机制 三.Dubbo IOC 总结 引言 Du ...

  6. Dubbo SPI源码解析①

    目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 ​ SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...

  7. Dubbo SPI 机制源码分析(基于2.7.7)

    Dubbo SPI 机制涉及到 @SPI.@Adaptive.@Activate 三个注解,ExtensionLoader 作为 Dubbo SPI 机制的核心负责加载和管理扩展点及其实现.本文以 E ...

  8. Dubbo2.7的Dubbo SPI实现原理细节

    总结/朱季谦 本文主要记录我对Dubbo SPI实现原理的理解,至于什么是SPI,我这里就不像其他博文一样详细地从概念再到Java SPI细细分析了,直接开门见山来分享我对Dubbo SPI的见解. ...

  9. 理解 Dubbo SPI 扩展机制

    写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...

  10. dubbo源码分析--dubbo spi解析

    1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射. 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件 ...

随机推荐

  1. python 图片转文字小工具

    应群友要求,要做一个图片转文字的格式,因为有些人的简历中只有一张图片要提取他里面的文字就不好办了. 于是就有了下面这个小工具: 功能:选择要识别的图片后,识别出来后存到.txt文本中. 实现原理,基于 ...

  2. fs.1.10 ON CENTOS7 docker镜像制作

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. centos7 docker上编译安装fs1.10版本的流程记录. 环境 docker engine:Version 24.0.6 ...

  3. MicroNet: 低秩近似分解卷积以及超强激活函数,碾压MobileNet | 2020新文分析

    论文提出应对极低计算量场景的轻量级网络MicroNet,包含两个核心思路Micro-Factorized convolution和Dynamic Shift-Max,Micro-Factorized ...

  4. DETR:Facebook提出基于Transformer的目标检测新范式,性能媲美Faster RCNN | ECCV 2020 Oral

    DETR基于标准的Transorfmer结构,性能能够媲美Faster RCNN,而论文整体思想十分简洁,希望能像Faster RCNN为后续的很多研究提供了大致的思路   来源:晓飞的算法工程笔记 ...

  5. Lab2:System Call

    trace 该系统调用程序,可以跟踪其他的系统调用命令,该系统调用的形参为一个整数掩码.其具体实参为1 << sys_call所得到的整数值,sys_call是一个系统调用指令在内核中定义 ...

  6. 关于Guava ForwardingMap

    ForwardingMap是什么? ForwardingMap 是一个装饰器 负责把所有的map操作转发到所代理的map. 操作转发是直接的,不经任何中间操作的. 对方法的覆写要慎重,比如关联的put ...

  7. Docker容器编排技术解析与实践

    本文全面探索了容器编排技术的核心概念.工具和高级应用,包括Docker Compose.Kubernetes等主要平台及其高级功能如网络和存储管理.监控.安全等.此外,文章还探讨了这些技术在实际应用中 ...

  8. OpenCV一句话将彩色图片(Mat)转换为灰度

    auto in_gray=cv::imdecode(in, cv::IMREAD_GRAYSCALE);

  9. 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition

    目录 简介 源码 函数说明 arv_camera_new arv_camera_acquisition arv_camera_get_model_name arv_buffer_get_image_w ...

  10. Hive 查看表/分区更新时间

    1.查看分区 hive> show partitions table_name; 2.查看分区更新时间 获取hdfs路径 hive> desc formatted table_name; ...