5-Dubbo Extension Adaptive 自适应扩展源码解析

作者: vnjohn / 发表于 2025-05-19 / 专栏: Dubbo 3.3

Dubbo3, 源码

5 创建自适应扩展对象 getAdaptiveExtension 方法

自适应扩展又称为动态扩展,可以在运行时生成扩展对象

ExtensionLoader#getAdaptiveExtension:该方法是第一个在源码中看到的获取扩展对象的方法,它可以帮助我们通过 SPI 机制从扩展文件中找到需要的扩展类型并创建其对象

自适应扩展:对设计模式比较熟悉的话,很容易就可以想到适配器模式,自适应扩展其实就是按照适配器的思路,自适应扩展有两种策略:

1、自身实现自适应扩展:使用 @Adaptive 修饰,此时适配器的逻辑由我们自己实现,当扩展加载器去查找具体的扩展时,可以通过找到我们这个适配器扩展,然后适配器扩展帮忙去查询真正的扩展类。比如后面要举例扩展注入器,具体扩展通过扩展注入适配器来查询具体的注入器扩展实现来帮忙查看扩展

2、未实现自适应扩展:Dubbo 在运行时通过字节码动态代理的方式在运行时生成一个适配器,使用这个适配器映射到具体的扩展,第二种情况往往用在 Protocol、Cluster、LoadBalance 等。有时有些扩展并不想在框架启动阶段被加载,而是希望在扩展方法被调用时,根据运行时参数进行加载

下面来具体看下 getAdaptiveExtension 方法源码是如何实现的,如下:

public T getAdaptiveExtension() {
  // 检查当前扩展器是否被销毁
  checkDestroyed();
  Object instance = cachedAdaptiveInstance.get();
  if (instance == null) {
    // 当第一个线程处理出现异常时,第二个线程会走到这里
    if (createAdaptiveInstanceError != null) {
      throw new IllegalStateException(
        "Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(),
        createAdaptiveInstanceError);
    }
    // Double Check Lock
    // 再次检查 cachedAdaptiveInstance,如果已经创建了实例,则直接返回,否则会再次创建实例
    synchronized (cachedAdaptiveInstance) {
      instance = cachedAdaptiveInstance.get();
      if (instance == null) {
        try {
          // 通过 SPI 机制获取类型,创建对象
          instance = createAdaptiveExtension();
          // 存入缓存
          cachedAdaptiveInstance.set(instance);
        } catch (Throwable t) {
          createAdaptiveInstanceError = t;
          throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
        }
      }
    }
  }
  return (T) instance;
}

使用单例思想来调用创建自适应扩展对象的方法,下面就让我们来深入探究下创建自适应扩展对象 createAdaptiveExtension 方法整个过程是如何实现的

5.1 创建扩展对象的生命周期方法

先来看 ExtensionLoader#createAdaptiveExtension 方法,该方法包含了扩展对象创建初始化的整个生命周期,如下代码所示:

private T createAdaptiveExtension() {
  try {
    // 获取扩展类型实现类, 创建扩展对象
    T instance = (T) getAdaptiveExtensionClass().newInstance();
    // 注入扩展对象之前的回调方法
    instance = postProcessBeforeInitialization(instance, null);
    // 注入扩展对象
    injectExtension(instance);
    // 注册扩展对象之后的回调方法
    instance = postProcessAfterInitialization(instance, null);
    // 初始化扩展对象的属性
    // 若当前扩展实例的类型实现了 Lifecycle 接口,则调用当前扩展对象的生命周期 initialize 方法
    // 参考样例: 第一个自适应扩展对象类型是 AdaptiveExtensionInjector,自适应扩展注入器(适配器)用来查询具体支持的扩展注入器
    // 比如 ScopeBeanExtensionInjector、SpiExtensionInjector、SpringExtensionInjector 注入器
    initExtension(instance);
    return instance;
  } catch (Exception e) {
    throw new IllegalStateException(
      "Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
  }
}

5.2 SPI 机制获取扩展对象实现类型

ExtensionLoader#getAdaptiveExtensionClass 方法可以帮助我们了解具体的 Dubbo SPI 机制

找到扩展类型的实现类以及会寻找那些文件,扩展文件的优先级又是什么,对我们自己写扩展方法很有帮助,下面具体来看下源码是如何实现的

private Class<?> getAdaptiveExtensionClass() {
  // 获取扩展类型,将扩展类型存入成员变量 cachedClasses 中进行缓存
  getExtensionClasses();
  // getExtensionClasses 方法解析中的最后一步 ExtensionLoader#loadClass 方法逻辑:
  // 若扩展类型存在 Adaptive 注解将会将扩展类型赋值给 cachedAdaptiveClass,否则的话会把扩展类型都缓存起来存储在扩展集合 extensionClasses 中
  if (cachedAdaptiveClass != null) {
    return cachedAdaptiveClass;
  }
  // 扩展实现类型没有这个自适应注解 Adaptive 时会走到这里
  // 刚刚扫描到扩展类型并将其存入到 cachedClasses 集合,接下来看下如何创建扩展类型
  return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

接下来继续看获取扩展类型的方法:getExtensionClasses

private Map<String, Class<?>> getExtensionClasses() {
  // 缓存中查询扩展类型是否存在
  Map<String, Class<?>> classes = cachedClasses.get();
  if (classes == null) {
    // 为了确保单例,通过 JUC Lock CAS 操作来保证线程安全
    loadExtensionClassesLock.lock();
    try {
      classes = cachedClasses.get();
      if (classes == null) {
        try {
          // 加载扩展类型
          classes = loadExtensionClasses();
        } catch (InterruptedException e) {
          logger.error(
            COMMON_ERROR_LOAD_EXTENSION,
            "",
            "",
            "Exception occurred when loading extension class (interface: " + type + ")",
            e);
          throw new IllegalStateException(
            "Exception occurred when loading extension class (interface: " + type + ")", e);
        }
        // 将扫描到的扩展类型存入成员变量 cachedClasses 中
        cachedClasses.set(classes);
      }
    } finally {
      loadExtensionClassesLock.unlock();
    }
  }
  return classes;
}

5.2.1 使用不同策略加载不同目录下的扩展

加载扩展类型的方法:loadExtensionClasses

加载文件优先级:DubboInternalLoadingStrategy > DubboLoadingStrategy > ServicesLoadingStrategy

private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
  // 检查扩展加载器是否被销毁
  checkDestroyed();
  // 缓存默认的扩展名到成员变量 cachedDefaultName 中
  cacheDefaultExtensionName();
  // 加载到的扩展集合
  Map<String, Class<?>> extensionClasses = new HashMap<>();
  /*
     目前存在三种 LoadingStrategy 扩展加载策略
     1 - ServicesLoadingStrategy     -> META-INF/services/
     2 - DubboLoadingStrategy       -> META-INF/dubbo/
     3 - DubboInternalLoadingStrategy -> META-INF/dubbo/internal/
   */
  for (LoadingStrategy strategy : strategies) {
    // 通过策略从指定文件目录中加载扩展类型
    loadDirectory(extensionClasses, strategy, type.getName());
    // 若当前要加载的扩展类型是扩展注入类型,则扫描 ExtensionFactory 类型的扩展
    // compatible with old ExtensionFactory
    if (this.type == ExtensionInjector.class) {
      // 通过 loadDirectory 扫描到了 ExtensionInjector 类型的扩展实现类有 3 个,将会得到这样一个集合例子:
      /*
         spring      ->  org.apache.dubbo.config.spring.extension.SpringExtensionInjector
         spi         ->  org.apache.dubbo.common.extension.inject.SpiExtensionInjector
         scopeBean   ->  org.apache.dubbo.common.beans.ScopeBeanExtensionInjector
       */
      loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
    }
  }
  return extensionClasses;
}

关于扩展策略的参数列表可以查看如下的表格:

扩展类型Directory
(目录)
extensionLoaderClassLoaderFirst
(优先扩展类型的类加载器)
DubboInternalLoadingStrategyMETA-INF/dubbo/internal/false
DubboLoadingStrategyMETA-INF/dubbo/false
ServicesLoadingStrategyMETA-INF/services/false

从文件中加载扩展实现 loadDirectory 方法

private void loadDirectory(Map<String, Class<?>> extensionClasses, LoadingStrategy strategy, String type) throws InterruptedException {
  // 加载并根据策略的参数来加载扩展类型
  loadDirectoryInternal(extensionClasses, strategy, type);
  // 是否开启 Dubbo2 Compact 兼容模式
  if (Dubbo2CompactUtils.isEnabled()) {
    try {
      // 兼容 alibaba 扩展包
      String oldType = type.replace("org.apache", "com.alibaba");
      if (oldType.equals(type)) {
        return;
      }
      // if class not found,skip try to load resources
      ClassUtils.forName(oldType);
      loadDirectoryInternal(extensionClasses, strategy, oldType);
    } catch (ClassNotFoundException classNotFoundException) {

    }
  }
}

根据策略的参数来加载扩展类型的方法:loadDirectoryInternal

private void loadDirectoryInternal(Map<String, Class<?>> extensionClasses, LoadingStrategy loadingStrategy, String type) throws InterruptedException {
  // 扩展目录 + 扩展类型全路径 比如: META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector
  String fileName = loadingStrategy.directory() + type;
  try {
    List<ClassLoader> classLoadersToLoad = new LinkedList<>();
    // try to load from ExtensionLoader's ClassLoader first
    // 是否优先使用扩展加载器的类加载器
    if (loadingStrategy.preferExtensionClassLoader()) {
      ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
      if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
        classLoadersToLoad.add(extensionLoaderClassLoader);
      }
    }
    // 获取特殊 SPI 加载策略,匹配 loadingStrategy 加载策略是否满足 special_spi.properties 文件中配置的加载策略
    if (specialSPILoadingStrategyMap.containsKey(type)) {
      String internalDirectoryType = specialSPILoadingStrategyMap.get(type);
      // skip to load spi when name don't match
      if (!LoadingStrategy.ALL.equals(internalDirectoryType)
          && !internalDirectoryType.equals(loadingStrategy.getName())) {
        return;
      }
      classLoadersToLoad.clear();
      classLoadersToLoad.add(ExtensionLoader.class.getClassLoader());
    } else {
      // load from scope model
      // 获取域模型对象的类型加载器,域模型对象在初始化时会将自己的类加载器放入集合中
      Set<ClassLoader> classLoaders = scopeModel.getClassLoaders();
      // 没有可用的类加载器能使用
      if (CollectionUtils.isEmpty(classLoaders)) {
        // 通过系统类加载器加载类的搜索路径中,指定名称的所有资源类
        Enumeration<java.net.URL> resources = ClassLoader.getSystemResources(fileName);
        if (resources != null) {
          while (resources.hasMoreElements()) {
            loadResource(
              extensionClasses,
              null,
              resources.nextElement(),
              loadingStrategy.overridden(),
              loadingStrategy.includedPackages(),
              loadingStrategy.excludedPackages(),
              loadingStrategy.onlyExtensionClassLoaderPackages());
          }
        }
      } else {
        classLoadersToLoad.addAll(classLoaders);
      }
    }
    // 使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源
    Map<ClassLoader, Set<java.net.URL>> resources = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad);
    // 遍历资源文件读取到资源 url 地址,key 为类加载器、值为扩展文件 url 如下所示
    // jar:file:/Users/song/.m2/repository/org/apache/dubbo/dubbo/3.0.7/dubbo-3.0.7.jar!/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector
    resources.forEach(((classLoader, urls) -> {
      // 从文件中加载完资源之后,开始根据类加载器和 url 加载具体的扩展类型,最后将扩展存放进 extensionClasses 集合
      loadFromClass(
        extensionClasses,
        loadingStrategy.overridden(),
        urls,
        classLoader,
        loadingStrategy.includedPackages(),
        loadingStrategy.excludedPackages(),
        loadingStrategy.onlyExtensionClassLoaderPackages());
    }));
  } catch (InterruptedException e) {
    throw e;
  } catch (Throwable t) {
    logger.error(
      COMMON_ERROR_LOAD_EXTENSION,
      "",
      "",
      "Exception occurred when loading extension class (interface: " + type + ", description file: "
      + fileName + ").",
      t);
  }
}

5.2.2 借助类加载器 loadResources 方法查询扩展文件

查找扩展类型对应的扩展文件 URL 方法源码:ClassLoaderResourceLoader#loadResources

public static Map<ClassLoader, Set<URL>> loadResources(String fileName, Collection<ClassLoader> classLoaders)
  throws InterruptedException {
  Map<ClassLoader, Set<URL>> resources = new ConcurrentHashMap<>();
  // 不同的类加载器之间使用不同的线程异步的方式进行扫描
  CountDownLatch countDownLatch = new CountDownLatch(classLoaders.size());
  for (ClassLoader classLoader : classLoaders) {
    // 多线程扫描,这个是个 newCachedThreadPool 类型的线程池
    GlobalResourcesRepository.getGlobalExecutorService().submit(() -> {
      resources.put(classLoader, loadResources(fileName, classLoader));
      countDownLatch.countDown();
    });
  }
  countDownLatch.await();
  return Collections.unmodifiableMap(new LinkedHashMap<>(resources));
}

具体类加载器加载资源文件的方法源码:loadResources

public static Set<URL> loadResources(String fileName, ClassLoader currentClassLoader) {
  Map<ClassLoader, Map<String, Set<URL>>> classLoaderCache;
  // 第一次进来类加载器资源缓存是空的
  if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) {
    // 类对象锁
    synchronized (ClassLoaderResourceLoader.class) {
      if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) {
        // 创建一个类资源映射 URL 软引用缓存对象
        // 软引用(soft references): 帮助垃圾收集器管理内存使用和消除潜在的内存泄漏,当内存快不足的时候,GC 会迅速的把所有的软引用清除掉,释放内存空间
        classLoaderCache = new ConcurrentHashMap<>();
        classLoaderResourcesCache = new SoftReference<>(classLoaderCache);
      }
    }
  }
  // 第一次进来时候类加载器 url 映射缓存是空的
  // 给类加载器缓存对象新增元素:key -> 类加载器、value -> map 类型用来存储文件名对应 url 集合
  if (!classLoaderCache.containsKey(currentClassLoader)) {
    classLoaderCache.putIfAbsent(currentClassLoader, new ConcurrentHashMap<>());
  }
  Map<String, Set<URL>> urlCache = classLoaderCache.get(currentClassLoader);
  // 缓存中没有就从文件里面找
  if (!urlCache.containsKey(fileName)) {
    Set<URL> set = new LinkedHashSet<>();
    Enumeration<URL> urls;
    try {
      // getResources 方法逻辑: 加载当前类加载器以及父类加载器所在路径的资源文件,将遇到的所有资源文件全部返回!
      // 这个可以理解为使用双亲委派模型中的类加载器,加载各个位置的资源文件
      urls = currentClassLoader.getResources(fileName);
      if (urls != null) {
        // 遍历找到的对应扩展的文件 url 将其加入集合
        while (urls.hasMoreElements()) {
          URL url = urls.nextElement();
          set.add(url);
        }
      }
    } catch (IOException e) {
      logger.error(
        COMMON_IO_EXCEPTION,
        "",
        "",
        "Exception occurred when reading SPI definitions. SPI path: " + fileName + " ClassLoader name: "
        + currentClassLoader,
        e);
    }
    // 存入缓存:filename -> Set<Url>
    urlCache.put(fileName, set);
  }
  return urlCache.get(fileName);
}

5.2.3 使用扩展资源 URL 加载具体扩展类型

遍历 URL 开始加载扩展类型方法源码:ExtensionLoader#loadFromClass

private void loadFromClass(
  Map<String, Class<?>> extensionClasses,
  boolean overridden,
  Set<java.net.URL> urls,
  ClassLoader classLoader,
  String[] includedPackages,
  String[] excludedPackages,
  String[] onlyExtensionClassLoaderPackages) {
  if (CollectionUtils.isNotEmpty(urls)) {
    for (java.net.URL url : urls) {
      loadResource(
        extensionClasses,
        classLoader,
        url,
        overridden,
        includedPackages,
        excludedPackages,
        onlyExtensionClassLoaderPackages);
    }
  }
}

ExtensionLoader#loadResource 方法:使用 IO 流读取扩展文件的内容,读取内容之前先贴一下我们要参考的扩展注入类型的文件内容,如下所示:

adaptive=org.apache.dubbo.common.extension.inject.AdaptiveExtensionInjector
spi=org.apache.dubbo.common.extension.inject.SpiExtensionInjector
scopeBean=org.apache.dubbo.common.beans.ScopeBeanExtensionInjector

扩展文件中的内容都是一行一行的,扩展名字和扩展类型之间使用等号 = 隔开,接下来继续来查看 loadResource 方法源码部分:

private void loadResource(
  Map<String, Class<?>> extensionClasses,
  ClassLoader classLoader,
  java.net.URL resourceURL,
  boolean overridden,
  String[] includedPackages,
  String[] excludedPackages,
  String[] onlyExtensionClassLoaderPackages) {
  try {
    List<String> newContentList = getResourceContent(resourceURL);
    String clazz;
    for (String line : newContentList) {
      try {
        String name = null;
        // 扩展文件名字和类型使用等号隔开,也可能是无类型的
        // 例如扩展加载策略使用的是 JDK 自带的方式 services 内容中只包含具体的扩展类型
        int i = line.indexOf('=');
        if (i > 0) {
          name = line.substring(0, i).trim();
          clazz = line.substring(i + 1).trim();
        } else {
          clazz = line;
        }
        /*
          sExcluded:是否为加载策略要排除的配置,参数这里为空代表全部类型不排除
          isIncluded:是否为加载策略包含的类型,参数这里为空代表全部文件皆可包含
          onlyExtensionClassLoaderPackages:参数是否只有扩展类的类加载器可以加载扩展,其他扩展类型的类加载器不能加载扩展,这里结果为 false,不排除任何类加载器
         */
        if (StringUtils.isNotEmpty(clazz)
            && !isExcluded(clazz, excludedPackages)
            && isIncluded(clazz, includedPackages)
            && !isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) {
          // 通过类全路径加载类到内存
          loadClass(
            classLoader,
            extensionClasses,
            resourceURL,
            Class.forName(clazz, true, classLoader),
            name,
            overridden);
        }
      } catch (Throwable t) {
        IllegalStateException e = new IllegalStateException(
          "Failed to load extension class (interface: " + type + ", class line: " + line + ") in "
          + resourceURL + ", cause: " + t.getMessage(),
          t);
        exceptions.put(line, e);
      }
    }
  } catch (Throwable t) {
    logger.error(
      COMMON_ERROR_LOAD_EXTENSION,
      "",
      "",
      "Exception occurred when loading extension class (interface: " + type + ", class file: "
      + resourceURL + ") in " + resourceURL,
      t);
  }
}

ExtensionLoader#loadClass 方法将加载具体的类到内存中

private void loadClass(
  ClassLoader classLoader,
  Map<String, Class<?>> extensionClasses,
  java.net.URL resourceURL,
  Class<?> clazz,
  String name,
  boolean overridden) {
  // 当前 clazz 是否为 type 子类型
  // 第一次访问到的 type -> ExtensionInjector, clazz -> SpringExtensionInjector 父子类型关系满足情况
  if (!type.isAssignableFrom(clazz)) {
    throw new IllegalStateException(
      "Error occurred when loading extension class (interface: " + type + ", class line: "
      + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface.");
  }
  boolean isActive = loadClassIfActive(classLoader, clazz);
  if (!isActive) {
    return;
  }
  // 扩展子类型是否存在这个注解@Adaptive
  if (clazz.isAnnotationPresent(Adaptive.class)) {
    cacheAdaptiveClass(clazz, overridden);
    // 扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的 Java IO 流中的类型使用到的装饰器模式 构造器传个类型)
  } else if (isWrapperClass(clazz)) {
    cacheWrapperClass(clazz);
  } else {
    // 无自适应注解,也没有构造器是扩展类型参数, name 在扩展文件中找到的话那就是等号前面那个值
    if (StringUtils.isEmpty(name)) {
      name = findAnnotationName(clazz);
      if (name.length() == 0) {
        throw new IllegalStateException("No such extension name for the class " + clazz.getName()
                                        + " in the config " + resourceURL);
      }
    }
    // 获取扩展名字数组,扩展名字可能为逗号隔开的
    String[] names = NAME_SEPARATOR.split(name);
    if (ArrayUtils.isNotEmpty(names)) {
      // 前面判断了若不是 Adaptive 也不是 Wrapper 类型则我们可以来判断是否为 Activate 类型
      // 如果是的话调用 cacheActivateClass 方法将扩展缓存进 cachedActivates 缓存中
      cacheActivateClass(clazz, names[0]);
      for (String n : names) {
        cacheName(clazz, n);
        // 将扩展类型加入结果集合 extensionClasses 中,不允许覆盖的话出现相同名字扩展将抛出异常
        saveInExtensionClass(extensionClasses, clazz, n, overridden);
      }
    }
  }
}

ExtensionLoader 类型中的 cacheActivateClass Adaptive 机制,即扩展类的自适应机制,其可以指定想要加载的扩展名,也可以不指定。

若不指定,则直接加载默认的扩展类,它会自动匹配,做到自适应,其是通过 @Adaptive 注解实现的,自适应注解修饰的扩展:同一个扩展名字只能有一个扩展实现类型,扩展策略中提供的参数 overridden 是否允许覆盖扩展\

private void cacheAdaptiveClass(Class<?> clazz, boolean overridden) {
  if (cachedAdaptiveClass == null || overridden) {
    // 成员变量存储这个自适应扩展类型
    cachedAdaptiveClass = clazz;
  } else if (!cachedAdaptiveClass.equals(clazz)) {
    throw new IllegalStateException(
      "More than 1 adaptive class found: " + cachedAdaptiveClass.getName() + ", " + clazz.getName());
  }
}

ExtensionLoader 类型中的 cacheWrapperClass Wrapper 机制,即扩展类的包装机制。其就是对扩展类的 SPI 接口方法进行增强,进行包装,是 AOP 思想的体现,属于 Wrapper 设计模式的应用,一个 SPI 可以包含多个 Wrapper,一个 Wrapper 也可以应用于多个 SPI

private void cacheWrapperClass(Class<?> clazz) {
  if (cachedWrapperClasses == null) {
    cachedWrapperClasses = new ConcurrentHashSet<>();
  }
  // 缓存这个 Wrapper 类型的扩展
  cachedWrapperClasses.add(clazz);
}

ExtensionLoader 类型中的 cacheActivateClass Activate 机制,即扩展类的激活机制。该扩展类型可以出现多个,比如:过滤器可以同一个扩展名出拥有多个过滤器实现,所以不需要有 override 判断 Activate 机制。通过指定的条件来激活当前的扩展类,其是通过 @Activate 注解实现的

private void cacheActivateClass(Class<?> clazz, String name) {
  Activate activate = clazz.getAnnotation(Activate.class);
  // 注解存在则加入激活注解缓存
  if (activate != null) {
    cachedActivates.put(name, activate);
  } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded()) {
    // support com.alibaba.dubbo.common.extension.Activate
    Annotation oldActivate = clazz.getAnnotation(Dubbo2ActivateUtils.getActivateClass());
    if (oldActivate != null) {
      cachedActivates.put(name, oldActivate);
    }
  }
}

处理 Activate 机制时会调用方法 ExtensionLoader#saveInExtensionClass,上面扩展对象加载了这么多最终的目的是为了将这个扩展类型存放进结果集合 extensionClasses 中,扩展策略中提供的参数 overridden 是否允许覆盖扩展

private void saveInExtensionClass(
  Map<String, Class<?>> extensionClasses, Class<?> clazz, String name, boolean overridden) {
  Class<?> c = extensionClasses.get(name);
  if (c == null || overridden) {
    // 上面扩展对象加载了这么多,最终的目的就是将这个扩展类型存放进结果集合中
    extensionClasses.put(name, clazz);
  } else if (c != clazz) {
    // duplicate implementation is unacceptable
    unacceptableExceptions.add(name);
    String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName()
      + " and " + clazz.getName();
    logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", duplicateMsg);
    throw new IllegalStateException(duplicateMsg);
  }
}

5.3 自适应扩展代理对象的代码生成与编译

若在 Dubbo 自适应机制中自身生成了自适应扩展的代理类

Dubbo 自适应扩展目的:在运行时动态调用扩展方法以及如何生成扩展代理类。比如:代理类中根据 URL 获取扩展名,使用 SPI 加载扩展类,并调用同名方法,返回执行结果

通过以上知识可得知 Dubbo 是如何通过扫描目录来查询扩展实现类的,当我们找到扩展类后,若这个扩展类型未加上 @Adaptive 注解那么是如何创建的呢

在 5.2 小节有说到 createAdaptiveExtensionClass 方法,该方法是借助字节码工具来动态生成所需扩展类型的包装代码,该代码在编译时可能看不到,但是在 Debug 时,还是可以看到这个对象名字的

当扩展点的方法被 @Adaptive 修饰时,在 Dubbo 初始化扩展点时会自动生成和编译一个动态的 Adaptive 类

interface org.apache.dubbo.rpc.Protocol 这个协议扩展类型来看,协议扩展类型是没有一个带有自适应注解的

private Class<?> createAdaptiveExtensionClass() {
  // Adaptive Classes' ClassLoader should be the same with Real SPI interface classes' ClassLoader
  // 获取加载器
  ClassLoader classLoader = type.getClassLoader();
  try {
    // native 配置 是否为本地镜像,参考官网: https://cn.dubbo.apache.org/zh-cn/docs/references/graalvm/support-graalvm/
    if (NativeDetector.inNativeImage()) {
      return classLoader.loadClass(type.getName() + "$Adaptive");
    }
  } catch (Throwable ignore) {

  }
  // 创建一个代码生成器来生成代码
  String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
  // 获取编译器
  org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector
    .getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class)
    .getAdaptiveExtension();
  // 编译生成的代码
  return compiler.compile(type, code, classLoader);
}

5.4 通过扩展对象 set 方法注入自适应扩展

在这里插入图片描述

继续回到 5.1 小节说到的 ExtensionLoader#createAdaptiveExtension 方法处,获取到自适应扩展类以后会调用 newInstance 方法进行实例化

T instance = (T) getAdaptiveExtensionClass().newInstance()

接下来看是如何给扩展对象中 set 方法注入自适应扩展对象的,调用方法代码如下:

// 注入扩展对象
injectExtension(instance);

ExtensionLoader#injectExtension 方法具体源码如下:

private T injectExtension(T instance) {
  // 若注入器为空则直接返回当前对象
  if (injector == null) {
    return instance;
  }
  try {
    // 获取当前类的所有方法
    for (Method method : instance.getClass().getMethods()) {
      // 方法条件:set 开头、参数只有一个、public 修饰
      if (!isSetter(method)) {
        continue;
      }
      // 方法上面是否有注解 DisableInject 修饰,这种情况也直接跳过
      if (method.isAnnotationPresent(DisableInject.class)) {
        continue;
      }
      // When spiXXX implements ScopeModelAware, ExtensionAccessorAware,
      // the setXXX of ScopeModelAware and ExtensionAccessorAware does not need to be injected
      if (method.getDeclaringClass() == ScopeModelAware.class) {
        continue;
      }
      if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
        if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
          continue;
        }
      }
      Class<?> pt = method.getParameterTypes()[0];
      // 方法的参数如果是原生类型也跳过
      if (ReflectUtils.isPrimitives(pt)) {
        continue;
      }
      try {
        // 获取 set 方法对应的成员变量,如:setProtocol 属性为 protocol
        String property = getSetterProperty(method);
        // 根据参数类型,如:Protocol 和属性名字,如:protocol 获取应该注入的对象
        Object object = injector.getInstance(pt, property);
        if (object != null) {
          // 执行对应对象和对应参数的这个方法
          method.invoke(instance, object);
        }
      } catch (Exception e) {
        logger.error(
          COMMON_ERROR_LOAD_EXTENSION,
          "",
          "",
          "Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": "
          + e.getMessage(),
          e);
      }
    }
  } catch (Exception e) {
    logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", e.getMessage(), e);
  }
  return instance;
}

5.4.1 获取注入对象

接着看如何通过注入器找到需要注入的那个对象,调用代码如下:

Object object = injector.getInstance(pt, property);

在 ExtensionLoader 构造方法中会获取 ExtensionInjector 接口的扩展实现类,其中自适应扩展注入器类型 AdaptiveExtensionInjector 就是以上的 getInstance 方法所在类,来具体看下实现源码:

public <T> T getInstance(final Class<T> type, final String name) {
  // 遍历所有的扩展注入器,如果可以获取到扩展对象则直接返回
  return injectors.stream()
    .map(injector -> injector.getInstance(type, name))
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(null);
}

AdaptiveExtensionInjector 在初始化时会将获取所有非适应的 ExtensonInjector 扩展实现,非自适应的的扩展列表一共有 3 个按顺序如下:

  • ScopeBeanExtensionInjector
  • SpiExtensionInjector
  • SpringExtensionInjector

下面来详细看下每种扩展注入器加载扩展对象的策略

5.4.2 域模型 Bean 扩展注入器

ScopeBeanExtensionInjector#getInstance 方法:每个域模型都会有个 ScopeBeanFactory 类型的对象用于存储共享对象,并且域模型之间按照层级子类型的 Bean 工厂可以从父域的 Bean 工厂中查询对象

public <T> T getInstance(final Class<T> type, final String name) {
  return beanFactory == null ? null : beanFactory.getBean(name, type);
}

ScopeBeanFactory#getBean 方法:先从当前域空间查询对象,若找不到对应类型的扩展对象,再从父域工厂查询扩展对象

public <T> T getBean(String name, Class<T> type) {
  // 当前域下注册的扩展对象
  T bean = getBeanFromCache(name, type);
  if (bean == null && parent != null) {
    // 父域中查找扩展对象
    return parent.getBean(name, type);
  }
  return bean;
}

ScopeBeanFactory#getBeanInternal 方法:从当前域下找注册的参数类型对象

private <T> T getBeanInternal(String name, Class<T> type) {
  // All classes are derived from java.lang.Object, cannot filter bean by it
  if (type == Object.class) {
    return null;
  }
  List<BeanInfo> candidates = null;
  BeanInfo firstCandidate = null;
  // 遍历当前域下注册的扩展对象,如果扩展对象类型是type的子类,则将其加入到 candidates 列表中
  for (BeanInfo beanInfo : registeredBeanInfos) {
    // if required bean type is same class/superclass/interface of the registered bean
    if (type.isAssignableFrom(beanInfo.instance.getClass())) {
      if (StringUtils.isEquals(beanInfo.name, name)) {
        return (T) beanInfo.instance;
      } else {
        // optimize for only one matched bean
        if (firstCandidate == null) {
          firstCandidate = beanInfo;
        } else {
          if (candidates == null) {
            candidates = new ArrayList<>();
            candidates.add(firstCandidate);
          }
          candidates.add(beanInfo);
        }
      }
    }
  }
  // if bean name not matched and only single candidate
  if (candidates != null) {
    if (candidates.size() == 1) {
      return (T) candidates.get(0).instance;
    } else if (candidates.size() > 1) {
      List<String> candidateBeanNames =
        candidates.stream().map(beanInfo -> beanInfo.name).collect(Collectors.toList());
      throw new ScopeBeanException("expected single matching bean but found " + candidates.size()
                                   + " candidates for type [" + type.getName() + "]: " + candidateBeanNames);
    }
  } else if (firstCandidate != null) {
    return (T) firstCandidate.instance;
  }
  return null;
}

5.4.3 SPI 扩展机制注入器

SpiExtensionInjector:SPI 是 Dubbo 自行实现的一套扩展机制,来具体看下源码是如何查找扩展对象的

public <T> T getInstance(final Class<T> type, final String name) {
  // 判断扩展点是否是接口且被 @SPI 注解
  if (!type.isInterface() || !type.isAnnotationPresent(SPI.class)) {
    return null;
  }
  // 使用扩展访问器来获取对应类型的扩展加载器
  ExtensionLoader<T> loader = extensionAccessor.getExtensionLoader(type);
  if (loader == null) {
    return null;
  }
  // 使用对应类型的扩展加载器来加载自适应扩展
  if (!loader.getSupportedExtensions().isEmpty()) {
    return loader.getAdaptiveExtension();
  }
  return null;
}

5.4.4 Spring 扩展注入器

SpringExtensionInjector:Spring 扩展注入器主要是用来从 Spring 容器中查询当前 Bean 是否存在

public <T> T getInstance(Class<T> type, String name) {
  if (context == null) {
    // ignore if spring context is not bound
    return null;
  }

  // check @SPI annotation 类型需要满足SPI机制 @SPI修饰的接口
  if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
    return null;
  }
  // 从 Spring 容器中获取对象
  T bean = getOptionalBean(context, name, type);
  if (bean != null) {
    return bean;
  }

  // logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " +
  // type.getName());
  return null;
}

private <T> T getOptionalBean(final ListableBeanFactory beanFactory, final String name, final Class<T> type) {
  // bean 名称为空,则根据类型获取 bean
  if (StringUtils.isEmpty(name)) {
    return getOptionalBeanByType(beanFactory, type);
  }
  // bean 名称不为空,则根据名称获取 bean
  if (beanFactory.containsBean(name)) {
    return beanFactory.getBean(name, type);
  }
  return null;
}

private <T> T getOptionalBeanByType(final ListableBeanFactory beanFactory, final Class<T> type) {
  String[] beanNamesForType = beanFactory.getBeanNamesForType(type, true, false);
  if (beanNamesForType == null) {
    return null;
  }
  if (beanNamesForType.length > 1) {
    throw new IllegalStateException("Expect single but found " + beanNamesForType.length
                                    + " beans in spring context: " + Arrays.toString(beanNamesForType));
  }
  // 通过 Bean Name、类型,查询具体的扩展对象
  return beanFactory.getBean(beanNamesForType[0], type);
}

getInstance —> getOptionalBean —> getOptionalBeanByType

5.5 自适应扩展类加载流程图

在这里插入图片描述

参考文献

https://www.ktyhub.com/zh/chapter_dubbo/5-getadaptiveextension/

vnjohn

作者

vnjohn

后端研发工程师。喜欢探索新技术,空闲时也折腾 AIGC 等效率工具。 可以在 GitHub 关注我了解更多,也可以加我微信(vnjohn) 与我交流。