1.原理
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。为了让大家对自适应拓展有一个感性的认识,下面我们通过一个示例进行演示。这是一个与汽车相关的例子,我们有一个车轮制造厂接口 WheelMaker:
public interface WheelMaker {
Wheel makeWheel(URL url);
}
WheelMaker 接口的自适应实现类如下:
public class AdaptiveWheelMaker implements WheelMaker {
public Wheel makeWheel(URL url) {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
// 1.从 URL 中获取 WheelMaker 名称
String wheelMakerName = url.getParameter("Wheel.maker");
if (wheelMakerName == null) {
throw new IllegalArgumentException("wheelMakerName == null");
}
// 2.通过 SPI 加载具体的 WheelMaker
WheelMaker wheelMaker = ExtensionLoader
.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);
// 3.调用目标方法
return wheelMaker.makeWheel(url);
}
}
AdaptiveWheelMaker 是一个代理类,与传统的代理逻辑不同,AdaptiveWheelMaker 所代理的对象是在 makeWheel 方法中通过 SPI 加载得到的。makeWheel 方法主要做了三件事情:
- 从 URL 中获取 WheelMaker 名称
- 通过 SPI 加载具体的 WheelMaker 实现类
- 调用目标方法
接下来,我们来看看汽车制造厂 CarMaker 接口与其实现类。
public interface CarMaker {
Car makeCar(URL url);
}
public class RaceCarMaker implements CarMaker {
WheelMaker wheelMaker;
// 通过 setter 注入 AdaptiveWheelMaker
public setWheelMaker(WheelMaker wheelMaker) {
this.wheelMaker = wheelMaker;
}
public Car makeCar(URL url) {
Wheel wheel = wheelMaker.makeWheel(url);
return new RaceCar(wheel, ...);
}
}
RaceCarMaker 持有一个 WheelMaker 类型的成员变量,在程序启动时,我们可以将 AdaptiveWheelMaker 通过 setter 方法注入到 RaceCarMaker 中。在运行时,假设有这样一个 url 参数传入:
dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker
RaceCarMaker 的 makeCar 方法将上面的 url 作为参数传给 AdaptiveWheelMaker 的 makeWheel 方法,makeWheel 方法从 url 中提取 wheel.maker 参数,得到 MichelinWheelMaker。之后再通过 SPI 加载配置名为 MichelinWheelMaker 的实现类,得到具体的 WheelMaker 实例。
上面的示例展示了自适应拓展类的核心实现 —— 在拓展接口的方法被调用时,通过 SPI 加载具体的拓展实现类,并调用拓展对象的同名方法。接下来,我们深入到源码中,探索自适应拓展类生成的过程。