前言

在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. 记录--JS精粹,原型链继承和构造函数继承的 “毛病”

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用! 咱常说,面向对象三大特点,封装.继承.多态. 这三个特点,以" ...

  2. mysql where子句 区分 大小写

    解决办法 使用binary关键字,此时就能区分大小写了 SELECT * FROM `tb_test` WHERE BINARY COL_1='abc'

  3. Kingbase ES函数参数模式与Oracle的异同

    文章概要: 本文对主要就KES和Oracle的PLSQL中关于存储过程参数模式异同进行介绍,列举和验证了存在的差异 (如果想直接看差异的结论可直接跳到末尾). 一,存储过程的三种参数模式 重新回顾一下 ...

  4. archlinux xfce 设置fcitx5中文输入法详细教程

    1.安装fcitx5 sudo pacman -S fcitx5-im fcitx5-chinese-addons fcitx5-pinyin-zhwiki fcitx5-im 包含fcitx5的一些 ...

  5. ET介绍——数值组件设计

    类似魔兽世界,moba这种技能极其复杂,灵活性要求极高的技能系统,必须需要一套及其灵活的数值结构来搭配.数值结构设计好了,实现技能系统就会非常简单,否则就是一场灾难.比如魔兽世界,一个人物的数值属性非 ...

  6. #线段树,离线#CF1000F One Occurrence

    题目 给定一个长度为\(n\)序列,\(m\)个询问,每次询问给定一个区间\([l,r]\), 如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出0 分析 考虑离线, ...

  7. JDK11的新特性:HTTP API和reactive streams

    目录 简介 怎么在java中使用reactive streams POST请求的例子 总结 JDK11的新特性:HTTP API和reactive streams 简介 在JDK11的新特性:新的HT ...

  8. 一个库帮你快速实现EF Core数据仓储模式

    前言 EF Core是我们.NET日常开发中比较常用的ORM框架,今天大姚要分享的内容是如何使用EF Core Generic Repository通用仓储库来快速实现EF Core数据仓储模式. E ...

  9. 民生直销银行终端安全修炼秘籍之HMS Core两大安全能力

    在金融行业数字化转型浪潮下,传统商业银行先后推出手机银行.直销银行等移动金融客户端.作为民生银行"数字金融"的试验田,民生直销银行始终秉承"简单的银行"发展理念 ...

  10. js实现电子白板

    功能:使用画笔绘制笔迹(线条).橡皮檫 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...