Dubbo SPI之自适应扩展机制 @Adaptive

上一篇介绍了 Dubbo SPI 的基本实现,这篇就介绍下 Dubbo SPI 的自适应扩展机制,对应注解 @Adaptive。

介绍

@Adaptive 定义如下:

public @interface Adaptive {
    /**
     * parameter names in URL
     */
    String[] value() default {};

}

value 是个字符数组,通过该属性从 URL 中获取扩展名,来决定使用哪个扩展。分为几种情况:

1.设置了 value,且从 URL 中找到了对应的扩展名,则使用该扩展;

2.设置了 value,但从 URL 中找不到扩展名,则使用默认的扩展,即 @SPI 中配置的 value,还是找不到则抛出异常;

3.未设置 value,则根据接口名生成 value,比如接口 YyyInvokerWrapper 生成 value = “yyy.invoker.wrapper”。

定义了 @Adaptive 注解的方法,参数列表中一定要有类型为 URL 的参数。

用法

按照 有无 @Adaptive 注解、注解设置不同 value,建立测试接口:

@SPI(value = "spi")
public interface SpiTest {

    String getName();

    @Adaptive
    int getAge(URL url);

    @Adaptive(value = "country")
    String getCountry(URL url);

    @Adaptive({"province", "city"})
    String getAddress(URL url);
}

使用方式:

@Test
public void adaptiveTest() {
    SpiTest spiTest = ExtensionLoader.getExtensionLoader(SpiTest.class).getAdaptiveExtension();
    System.out.println(spiTest);
}

原理

首先根据接口获取 ExtensionLoader 对象,Dubbo 会缓存 ExtensionLoader 和接口的 Class 对象的映射关系。

接着调用 getAdaptiveExtension() 获取自适应扩展。同样,Dubbo 会缓存自适应扩展对象,但第一次会调用 createAdaptiveExtension() 创建自适应扩展对象。主要逻辑如下:

image.png

创建自适应扩展的步骤分为三步:
1.调用 getAdaptiveExtensionClass() 获取自适应扩展类的 Class 对象;
2.调用 Class.newInstance() 反射创建对象;
3.调用 injectExtension 为扩展对象注入依赖。

其中第 3 步是 Dubbo 实现的 IOC 机制,会为对象自动注入依赖,可参看另一篇讲述 Dubbo SPI 机制的文章。

本篇重点关注第 1 步。

加载所有扩展类

实现如下:
Dubbo SPI之自适应扩展机制 @Adaptive_第1张图片

首先调用 getExtensionClass() 加载所有扩展类,即 Dubbo SPI 的基础功能。需要注意的是,在加载具体扩展实现类的时候,如果类上有 @Adaptive 注解,会标记为 cachedAdaptiveClass:

Dubbo SPI之自适应扩展机制 @Adaptive_第2张图片

如果有 cachedAdaptiveClass,则直接返回;否则自动创建自适应扩展类。

创建自适应扩展类

实现如下:
image.png

这里分为两步:
1.根据接口信息和 SPI 信息动态生成代理类 Java 代码;
2.调用 Compiler 接口将编译 Java 代码。

动态生成代理类代码

Dubbo 通过调用 AdaptiveClassCodeGenerator#generate() 生成 Java 代码:

Dubbo SPI之自适应扩展机制 @Adaptive_第3张图片

生成的 Java 代码主要有:package、import、class 声明、遍历生成方法信息等。

这里要注意的是,通过自适应扩展机制生成的类名,都以 “$Adaptive” 结尾:

String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n";

具体的逻辑就不展开,读者可自行了解。SpiTest 类生成的 Java 代码示例如下:

Dubbo SPI之自适应扩展机制 @Adaptive_第4张图片

动态生成的代理类,获取扩展实现类时,根据设置 @Adaptive 的不同分为以下几种情况:

1.未设置 @Adaptive 注解,不支持调用,会抛出 UnsupportedOperationException 异常;

2.只设置 @Adaptive,未设置 value,根据接口名生成扩展名,即 “spi.test”,默认值为 @SPI 定义的 value;

3.设置 @Adaptive(value=“country”), 根据 “country” 从 URL 中获取扩展名,默认值为 @SPI 定义的 value;

4.设置 @Adaptive({“province”, “city”}),先根据 “city” 从 URL 获取配置 value1,默认值为 @SPI 定义的 value;再根据 “province” 从 URL 获取配置 value2,默认值为 value1。

可以看到,@Adaptive 的 value 值如果设置为数组,则按照倒序从 URL 获取配置,每一个配置都是下一个配置的默认值。

编译代理类代码

编译代码使用了 Compiler 接口,也是通过自适应扩展机制获取。Dubbo 提供了 Javasist 和 JDK 两种编译器实现。

Dubbo SPI之自适应扩展机制 @Adaptive_第5张图片

先根据 ClassName 从类加载器获取类信息,如果获取不到,再将前面生成的 Java 代码编译成字节码。

编译 Java 代码的实现不是本篇重点,读者可自行了解。

总结

Dubbo 通过 @Adaptive 注解定义自适应扩展信息,可定义在类和方法上:如果定义在类上,则直接使用该类获取扩展实现类;如果定义在方法上,则动态生成 Java 代码,并编译加载到类加载器,内部根据 value 的不同,从 URL 获取配置信息,决定使用哪个扩展实现类。

你可能感兴趣的:(Dubbo)