Dubbo-扩展机制

一、基本概述

前言

Dubbo 框架设计采用了[微内核+插件]的方式,以此来保证框架整体的灵活性,在提升可定制性的同时,避免了自身的臃肿。通过将原本与内核集成在一起的组件分离出来,只提供了特定的接入接口,组件可以独立的发展、更改而不会对现有系统造成改动。

Dubbo 的扩展机制基于 Java 的 SPI,但又不同于它。这里由于篇幅缘故,不再介绍 JDK 的 SPI 机制。它具有以下特点:

  • Dubbo 不会一次性加载(实例化)扩展所有实现,从而避免空间和时间的浪费。
  • Dubbo 增加了对扩展点 IOC 和 AOP 的支持。

概念

再来对几个概念进行解释:

1.扩展点

简单来说即接口,准确一点来说是拥有 @SPI 注解的接口,透过扩展点,可以加载各种不同的扩展类。

2.扩展加载器

不同扩展点拥有各自的扩展加载器,在 Dubbo 中, ExtensionLoader 表示一个扩展加载器。

3.扩展适配实例&扩展适配类

这里我们可以把扩展适配器当成是接口的工厂类,在生成扩展时,它可以采取对应的策略生成不同的扩展实例。

[扩展适配类]其实就是[扩展适配实例]的 Class 类型。在 Dubbo 中,可以被申明为扩展适配类的类具有以下两种特点,满足其一即可:

  • 实现自扩展点,且扩展点的方法具有 @Adaptive 注解,该方式在运行时动态生成,即随处可见 xxx$Active 类。
  • 实现自扩展点,且该具有 @Adaptive 注解,常见的如 AdaptiveExtensionFactory 等。

4.扩展实例&扩展类

如上所言,扩展类是扩展的实例的 Class 类型,它代表着最终我们要生成的实例。

5.扩展装饰类

扩展装饰类,其实是对扩展类的代理,通过这种装饰器模式,以此来实现对扩展类的 AOP 。


比较

总的来说,我们可以把 Dubbo 的扩展机制,理解成是工厂模式的另类实现。但它比工厂模式来的更为灵活,优雅。

以普通接口的工厂模式为例:

// 接口
public interface Person {
   
   
    void say();
}

// 实现类
public class Man implements Person {
   
   
    public void say() {
   
   
        System.out.println("this is a man");
    }
}
public class Woman implements Person {
   
   
    public void say() {
   
   
        System.out.println("this is a woman");
    }
}
public class NullPerson implements Person {
   
   
    public void say() {
   
   
        System.out.println("this is null");
    }
}

// 工厂类
public class PersonFactory implements Person {
   
   
    public static Person create(String sex) {
   
   
        if ("man".equals(sex)) {
   
   
            return new Man();
        } else if ("woman".equals(sex)) {
   
   
            return new Woman();
        }
        return new NullPerson();
    }
}

// 调用
PersonFactory.create("man").say();

再来看看换成 Dubbo 会是怎样的实现过程:

// 具体细节暂时不表述,总的来说都是这样的调用方式
ExtensionLoader.getExtensionLoader(Person.class).getAdaptiveExtension().say();

二、实现过程

上面介绍了 Dubbo 的大致情况,下面再来探究下它的具体实现过程。到目前为止,我们大致有了以下概念:

  • 扩展点即接口
  • 扩展适配类即工厂类
  • 扩展类即接口的具体实现类
  • 扩展装饰类即实现类的装饰类
  • 扩展机制具有 Ioc,Aop 的特性

在 Dubbo 中,整个扩展机制的核心即 ExtensionLoader 类。该类包含了 Dubbo 扩展机制的具体实现。在 Dubbo 中,要完成一次方法调用,大致需要经过以下步骤:

  • 生成扩展加载器(getExtensionLoader)
  • 生成扩展适配类(getAdaptiveExtensionClass)
  • 生成扩展适配实例(getAdaptiveExtension)
  • 生成扩展实例(getExtension),该过程发生在执行方法时

接着来看具体的步骤:


生成扩展加载器

生成扩展加载器,即通过扩展点获取创建指定的 ExtensionLoader。上面提到,不同的扩展点都有不同的扩展加载器,虽然具有多个实例,但是它们共享缓存

调用如下:

ExtensionLoader.getExtensionLoader(XXX.class)

具体步骤如下:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
   
   
    // 1.校验
    if (type == null)// 抛出异常...if (!type.isInterface()) {
   
   
       // 抛出异常...
    }
    // 判断是否存在 SPI 注解,即判断该类是否为扩展点
    if (!withExtensionAnnotation(type)) {
   
   
        // 抛出异常...
    }
    
    // 2.获取 
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
   
   
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

扩展器的生成过分为了两个部分:校验、创建

  • 校验:要求被的扩展的类型(即扩展点)不能为空,且必须是被 SPI 注解的接口
  • 生成:扩展器的生成使用了缓存机制,先从缓存中获取,若没有再创建新的扩展加载器

接着来看具体的生成过程:

1.从缓存获取

// EXTENSION_LOADERS 本质是一个 ConcurrentMap
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = 
    new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);

2.新建

// 变量
private final Class<?> type;
private final ExtensionFactory objectFactory;

// 构造器
private ExtensionLoader(Class<?> type) {
   
   
    this.type = type;
    // 先利用扩展机制生成 ExtensionFactory 的扩展适配实例
    // 且当 type 为 ExtensionFactory 时,它的扩展器扩展器变量 objectFactory 为 null,防止无限递归
    objectFactory = (type == ExtensionFactory.class ? null : 
    	ExtensionLoader.getExtensionLoader(ExtensionFactory.class).
			getAdaptiveExtension());
}

生成扩展适配类

生成扩展适配类,发生在生成[扩展适配实例]的过程中。与此同时,它还会获取该扩展的所有实现类,然后按照作用分成三类放进缓存,分别是:

  • 扩展适配类
  • 扩展类
  • 扩展装饰类

具体步骤如下:

private Class<?> getAdaptiveExtensionClass() {
   
   
    // 1.获取,先从缓存->再从指定目录加载
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
   
   
        return cachedAdaptiveClass;
    }
    // 2.创建
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

在获取扩展适配类时,同样采用了缓存机制:

1.从缓存获取

// 这里存储扩展适配类的缓存本质一个 Holder ,它存储了一组 <k,v>,且它是非线程安全的
private final Holder<Map<String, Class<?>>> cachedClasses = 
    new Holder<Map<String, Class<?>>>();

private Map<String, Class<?>> getExtensionClasses() {
   
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值