前言
前面两篇文章对dubbo SPI的使用和原理进行简单的讲解,大家应该对dubbo SPI有了认识。在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。
现在我们再来回顾一下dubbo SPI的简单使用,代码如下:
// 1、先获取 extensionLoader
ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
// 2、根据名称获取对应的扩展点instance
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
// 3、调用对应扩展点instance的方法
optimusPrime.sayHello();
这段代码演示了dubbo SPI的使用,但是有一个问题,扩展点对应的实现类不能在程序运行时动态指定,就是extensionLoader.getExtension
方法写死了扩展点对应的实现类,不能在程序运行期间根据运行时参数进行动态改变。
对于这个矛盾的问题,就有引入我们今天要将的Adaptive
,就是dubbo 的自适应机制。Dubbo 通过自适应拓展机制很好的解决了。我们首先看一下如果是我们,怎么解决这个矛盾,下面给出示例:
- 这里先定义一个接口,取名叫
AdaptiveExt
, 这里需要加一个com.alibaba.dubbo.common.extension.SPI
的注解
@SPI
public interface AdaptiveExt {
String echo(String msg, URL url);
}
这里的URL
类为dubbo里面的URL
- 接着创建一个实现类
SpringAdaptiveExtImpl
,按照dubbo SPI 配置,配置好
public class SpringAdaptiveExtImpl implements AdaptiveExt {
@Override
public String echo(String msg, URL url) {
return "spring";
}
}
- 重点来了,这一步我们解决扩展点对应的实现类不能在程序运行时动态指定的问题,这里我们自己编写一个
AdaptiveExt
自适应实现类,如下:
public class AdaptiveExtProxy implements AdaptiveExt{
@Override
public String echo(String msg, URL url) {
// 非空检查
if (url == null) {
throw new IllegalArgumentException("url == null");
}
// 1、从 URL 中获取 AdaptiveExt 名称
String name = url.getParameter("adaptive.ext");
if (name == null ) {
throw new IllegalArgumentException("name == null");
}
// 2、调用 SPI 动态加载具体的 AdaptiveExt
AdaptiveExt adaptiveExt = ExtensionLoader
.getExtensionLoader(AdaptiveExt.class)
.getExtension(name);
// 3、调用具体的方法
return adaptiveExt.echo(msg, url);
}
}
AdaptiveExtProxy
其实是一个代理类,AdaptiveExtProxy
所代理的对象是在echo
方法中通过SPI加载得到的,echo
方法主要做了三件事情:
- 从 URL 中获取
AdaptiveExt
名称 - 通过 SPI 加载具体的
AdaptiveExt
实现类 - 调用目标方法
- 进行测试,测试代码如下:
@Test
public void test() {
URL url = URL.valueOf("test://localhost/test?adaptive.ext=spring");
AdaptiveExt adaptiveExt = new AdaptiveExtProxy();
System.out.println(adaptiveExt.echo("e", url));
}
输出结果:
spring
Process finished with exit code 0
到这里,我们自己基本上解决了上面所说的矛盾。大家大致知道 dubbo SPI 扩展点对应的实现类,在程序运行期间根据运行时参数进行动态改变的解决方案了。就是设置一个代理类,代理类根据程序运行期间URL的参数,动态加载响应的实现类并代理。
dubbo Adaptive的使用
刚才我们简单讲了dubbo的自适应拓展机制是啥,现在我们开学习dubbo 自适应机制的学习。dubbo 自适应机制的使用是通过注解的方式,注解为com.alibaba.dubbo.common.extension.Adaptive
。直接上代码示例:
- 首先定义一个接口,名称为
AdaptiveExt
,使用SPI
和Adaptive
修饰:
@SPI
public interface AdaptiveExt {
@Adaptive
String echo(String msg, URL url);
}
- 创建三个实现类,分别为
DubboAdaptiveExtImpl
、SpringAdaptiveExtImpl
和SpringBootAdaptiveExtImpl
public class DubboAdaptiveExtImpl implements AdaptiveExt {
@Override
public String echo(String msg, URL url) {
return "dubbo";
}
}
public class SpringAdaptiveExtImpl implements AdaptiveExt {
@Override
public String echo(String msg, URL url) {
return "spring";
}
}
public class SpringBootAdaptiveExtImpl implements AdaptiveExt{
@Override
public String echo(String msg, URL url) {
return "spring boot";
}
}
- 接着在META-INF/dubbo文件夹下创建一个文件,名称为
Robot
的全限定名com.hui.wang.dubbo.learn.dubbo.adaptive.AdaptiveExt
,内容如下:
dubbo=com.hui.wang.dubbo.learn.dubbo.adaptive.DubboAdaptiveExtImpl
spring=com.hui.wang.dubbo.learn.dubbo.adaptive.SpringAdaptiveExtImpl
springboot=com.hui.wang.dubbo.learn.dubbo.adaptive.SpringBootAdaptiveExtImpl
到这里,示例的基本代码搭建完成,现在开始进入使用和测试
- 在URL里面指定参数,代码为:
@Test
public void test1() {
ExtensionLoader<AdaptiveExt> extExtensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);
AdaptiveExt adaptiveExt = extExtensionLoader.getAdaptiveExtension();
URL url = URL.valueOf("test://localhost/test?adaptive.ext=spring");
System.out.println(adaptiveExt.echo("d", url));
}
打印结果为:
spring
可以看出,当URL中参数指定相应的扩展点实现类名称后,就可以根据名称动态的调用响应名称的实现类。
- 配置
Adaptive
注解的value
值,代码如下:
@SPI
public interface AdaptiveExt {
@Adaptive(value = {"myAdaptiveName"})
String echo(String msg, URL url);
}
测试代码:
@Test
public void test4() {
ExtensionLoader<AdaptiveExt> extExtensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);
AdaptiveExt adaptiveExt = extExtensionLoader.getAdaptiveExtension();
URL url = URL.valueOf("test://localhost/test?myAdaptiveName=spring");
System.out.println(adaptiveExt.echo("d", url));
}
打印结果为:
spring
在方法上打上@Adaptive
注解,注解中的value
与链接中的参数的key
一致,链接中的key
对应的value
就是spi中的name
,获取相应的实现类。
3.@Adaptive
注解使用在实现类上,代码如下:
@Adaptive
public class DubboAdaptiveExtImpl implements AdaptiveExt {
@Override
public String echo(String msg, URL url) {
return "dubbo";
}
}
测试代码:
@Test
public void test4() {
ExtensionLoader<AdaptiveExt> extExtensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);
AdaptiveExt adaptiveExt = extExtensionLoader.getAdaptiveExtension();
URL url = URL.valueOf("test://localhost/test?myAdaptiveName=spring");
System.out.println(adaptiveExt.echo("d", url));
}
打印结果为:
dubbo
这里可以看到实现类上有@Adaptive
注解后,默认调用该类进行调用。
到这里基本上就是dubbo 自适应机制的使用,即@Adaptive
注解的使用,在下一篇我们将介绍@Adaptive
注解背后的源码实现