1.原理

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。为了让大家对自适应拓展有一个感性的认识,下面我们通过一个示例进行演示。这是一个与汽车相关的例子,我们有一个车轮制造厂接口 WheelMaker:

  1. public interface WheelMaker {
  2. Wheel makeWheel(URL url);
  3. }

WheelMaker 接口的自适应实现类如下:

  1. public class AdaptiveWheelMaker implements WheelMaker {
  2. public Wheel makeWheel(URL url) {
  3. if (url == null) {
  4. throw new IllegalArgumentException("url == null");
  5. }
  6. // 1.从 URL 中获取 WheelMaker 名称
  7. String wheelMakerName = url.getParameter("Wheel.maker");
  8. if (wheelMakerName == null) {
  9. throw new IllegalArgumentException("wheelMakerName == null");
  10. }
  11. // 2.通过 SPI 加载具体的 WheelMaker
  12. WheelMaker wheelMaker = ExtensionLoader
  13. .getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);
  14. // 3.调用目标方法
  15. return wheelMaker.makeWheel(url);
  16. }
  17. }

AdaptiveWheelMaker 是一个代理类,与传统的代理逻辑不同,AdaptiveWheelMaker 所代理的对象是在 makeWheel 方法中通过 SPI 加载得到的。makeWheel 方法主要做了三件事情:

  1. 从 URL 中获取 WheelMaker 名称
  2. 通过 SPI 加载具体的 WheelMaker 实现类
  3. 调用目标方法

接下来,我们来看看汽车制造厂 CarMaker 接口与其实现类。

  1. public interface CarMaker {
  2. Car makeCar(URL url);
  3. }
  4. public class RaceCarMaker implements CarMaker {
  5. WheelMaker wheelMaker;
  6. // 通过 setter 注入 AdaptiveWheelMaker
  7. public setWheelMaker(WheelMaker wheelMaker) {
  8. this.wheelMaker = wheelMaker;
  9. }
  10. public Car makeCar(URL url) {
  11. Wheel wheel = wheelMaker.makeWheel(url);
  12. return new RaceCar(wheel, ...);
  13. }
  14. }

RaceCarMaker 持有一个 WheelMaker 类型的成员变量,在程序启动时,我们可以将 AdaptiveWheelMaker 通过 setter 方法注入到 RaceCarMaker 中。在运行时,假设有这样一个 url 参数传入:

  1. dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker

RaceCarMaker 的 makeCar 方法将上面的 url 作为参数传给 AdaptiveWheelMaker 的 makeWheel 方法,makeWheel 方法从 url 中提取 wheel.maker 参数,得到 MichelinWheelMaker。之后再通过 SPI 加载配置名为 MichelinWheelMaker 的实现类,得到具体的 WheelMaker 实例。

上面的示例展示了自适应拓展类的核心实现 —— 在拓展接口的方法被调用时,通过 SPI 加载具体的拓展实现类,并调用拓展对象的同名方法。接下来,我们深入到源码中,探索自适应拓展类生成的过程。