🍎 Dubbo SPI 之扩展点自动包装 Wrapper 类

之前的文章我们分析了 Dubbo 的扩展点自适应机制。Dubbo SPI 主要思想也是来自于 JDK 原生的 SPI 机制。框架定义好扩展点接口,服务提供者实现接口。框架可以通过SPI机制动态的将服务提供商切入到应用中。使我们的程序可以面向接口,对扩展开放。

Dubbo SPI 与 JDK SPI

JDK SPI 需要在 classpathMETA-INF/services 目录下创建以扩展点接口全限定名命名的文件,里面内容为实现类的名称(完整包名),多个实现类换行分隔。
文件名称:

spi.png

spi内容.png

JDK加载扩展点实现类的方式:

Iterator<Registry> registryImpls = ServiceLoader.load(Registry.class).iterator();

结论
JDK SPI 会加载 classpath 下的所有 META-INF/services 下的所有接口对应的实现类。如果该实现类在 classpath 存在,则会通过ServiceLoader加载出来。如果有多个实现类,多个实现类都会被加载出来,用户则会选择使用哪个。

Dubbo SPI 改进

  • 不一次性实例化扩展点所有实现,再用到的时候,再选择性加载,可以减少资源浪费。
  • 扩展点加载失败异常跑出更明确的信息
  • 提供扩展点实现类与实现类之间的Wrapper 操作,可以用来聚合公共部分逻辑。
  • 提供 IOCAOP 等功能。

Dubbo 扩展点包装 Wrapper 类

一个典型的Wrapper类如下:

public class CommonRegistry implements Registry {
   // 持有 扩展点接口
   private Registry registry;
   // 构造器注入
   public CommonRegistry(Registry registry) {
       this.registry = registry;
   }

   @Override
   public String register(URL url, String msg) {
       // doSomething
      return  registry.register(url,msg);
   }

   @Override
   public String discovery(URL url, String content) {
      // doSomething
      return  registry.register(url,msg);
   }
}

分析上面的包装类,我们得出 Dubbo 认为的包装类需要满足的两个条件

  • 1.持有扩展点接口对象属性,并通过构造器方式初始化该属性
  • 2.这个类也要实现扩展点接口类,并在实现方法中进行增强操作

Wrapper 包装类实战

上一篇文章 [Dubbo SPI 之 Adaptive 自适应类] 我们介绍过两个注册中心的实现。这里我们有一个新的需求,需要对每一个注册中心的注册服务和发现服务统计一下耗时,增加一些共有逻辑。所以我们定义一个 Wrapper 类。

public class CommonRegistry implements Registry {
    private Logger logger = LoggerFactory.getLogger(CommonRegistry.class);
    // 持有 扩展点接口
    private Registry registry;
    // 构造器注入
    public CommonRegistry(Registry registry) {
        this.registry = registry;
    }

    @Override
    public String register(URL url, String msg) {
        long begin = System.currentTimeMillis();
        String register = registry.register(url, msg);
        long end = System.currentTimeMillis();
        logger.info("register method 处理耗时 cost: {} ms", end - begin);
        return register;
    }

    @Override
    public String discovery(URL url, String content) {
      //...实现同上
    }
}

META-INF/dubbo/com.maple.spi.Registry里面增加 wrapper 类的信息:

common=com.maple.spi.impl.CommonRegistry

测试 Main

public static void main(String[] args) {
     URL url = URL.valueOf("test://localhost/test")
                  .addParameter("service", "helloService")
                  .addParameter("registry","etcd");
                  //.addParameter("registry","zookeeper");
    
     Registry registry = ExtensionLoader.getExtensionLoader(Registry.class)
                                        .getAdaptiveExtension();

     System.out.println(registry.register(url, "maple"););
}

分别尝试使用 etcdzookeeper,控制台打印如下:

//Etcd
09-26 00:55:16 707 main INFO - 服务: helloService 已注册到 Etcd 上,备注: maple
09-26 00:55:16 709 main INFO - register method 处理耗时 cost: 2 ms
Etcd register already! 
// zookeeper
09-26 00:56:17 282 main INFO - 服务: helloService 已注册到zookeeper上,备注: maple
09-26 00:56:17 284 main INFO - register method 处理耗时 cost: 2 ms
Zookeeper register already! 

我们看到了 register method 处理耗时 cost: 2 ms 这条日志,说明 CommonRegistry 的逻辑已经运行了。程序先构造 CommonRegistry,从它的构造器中传入的是扩展点实现类,程序会先调用wrapper类对应的方法, 然后在方法内部再调用扩展点实现类的对应方法。类似于装饰器模式,为扩展点实现类增强了功能。
通过这种设计模式,我们可以将多个扩展点实现共用的公共逻辑都移到此类中来。

Wrapper 类不属于候选的扩展点实现

Wrapper 类不属于扩展点实现,我们可以通过如下代码进行验证:

Set<String> extens = ExtensionLoader
                        .getExtensionLoader(Registry.class)
                        .getSupportedExtensions();
//结果
[etcd, zookeeper]

通过 getSupportedExtensions 可以获取扩展点接口 Registry 当前所有的服务扩展实现的 key 值。控制台的结果只有 etcdzookeeper。 因此,wrapper 不属于 扩展点实现,同理 上一篇文章介绍的自适应类 Adaptive, 也不属于扩展点实现。

总结

通过本文总结 JDK SPI 原理和使用方式,然后和 Dubbo SPI 进行对比。

Dubbo 扩展点自动包装Wrapper类,类似与AOP,为扩展点实现增加更多前置或者后置功能模块。实现原理采用装饰器设计模式,将真正的扩展点实现包装在Wrapper类中。扩展点的Wrapper可以有多个,可以根据需求新增。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,791评论 6 545
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,795评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,943评论 0 384
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 64,057评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,773评论 6 414
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,106评论 1 330
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,082评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,282评论 0 291
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,793评论 1 338
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,507评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,741评论 1 375
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,220评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,929评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,325评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,661评论 1 296
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,482评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,702评论 2 380

推荐阅读更多精彩内容