DubboSPI

发布于:2025-06-23 ⋅ 阅读:(14) ⋅ 点赞:(0)

org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses

  • Dubbo扩展点加载机制的核心方法,负责从配置文件中读取并解析所有的扩展点实现类。
  • 理解Dubbo SPI机制的核心入口
private Map<String, Class<?>> getExtensionClasses() {
  // 从缓存中读取已加载的扩展类
  Map<String, Class<?>> classes = cachedClasses.get();
  if (classes == null) {
    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.set(classes);
      }
    } finally {
      loadExtensionClassesLock.unlock();
    }
  }
  return classes;
}

org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses

  • 加载扩展类
private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
  // 检查扩展加载器是否已被销毁,防止在应用关闭过程中继续加载扩展点
  checkDestroyed();
  // 缓存默认扩展点名称(通过 @SPI 注解指定)
  cacheDefaultExtensionName();

  Map<String, Class<?>> extensionClasses = new HashMap<>();

  // 遍历所有加载策略
  // LoadingStrategy:用于定义扩展点加载策略
  // 		getDirectory():返回扩展实现的配置文件所在目录(如 META-INF/dubbo/)
  // 		getPriority():定义加载策略的优先级,数值越小优先级越高
  // 默认实现类
  // 		DubboInternalLoadingStrategy:加载Dubbo内部组件,路径为 META-INF/dubbo/internal/
	// 		DubboLoadingStrategy:加载用户自定义扩展,路径为 META-INF/dubbo/
	// 		ServicesLoadingStrategy:兼容 JDK SPI 标准,路径为 META-INF/services/
  for (LoadingStrategy strategy : strategies) {
    // 从当前策略指定的路径加载扩展类
    loadDirectory(extensionClasses, strategy, type.getName());

    // 兼容旧版本的 ExtensionFactory 实现
    if (this.type == ExtensionInjector.class) {
      loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
    }
  }

  return extensionClasses;
}

org.apache.dubbo.common.extension.ExtensionLoader#loadDirectory

  • 从特定目录加载扩展类的配置文件,并将其映射关系存储到 extensionClasses
  • 为了兼容从 com.alibaba 迁移到 org.apache 的类路径变更,它会尝试加载旧路径下的同名类
private void loadDirectory(Map<String, Class<?>> extensionClasses, LoadingStrategy strategy, String type)
  throws InterruptedException {
  // 实际执行加载逻辑的内部方法,它会根据 strategy 提供的目录(如 META-INF/dubbo/)读取配置文件,并将扩展名与实现类的映射存入 extensionClasses
  loadDirectoryInternal(extensionClasses, strategy, type);
  // 用于判断是否启用 Dubbo 2.x 兼容性模式
  if (Dubbo2CompactUtils.isEnabled()) {
    try {
      // 将类路径中的 org.apache 替换为 com.alibaba(例如 org.apache.dubbo.xxx → com.alibaba.dubbo.xxx)
      String oldType = type.replace("org.apache", "com.alibaba");
      if (oldType.equals(type)) {
        return;
      }
      // 通过 ClassUtils.forName(oldType) 检查旧类是否存在,若不存在则跳过加载
      ClassUtils.forName(oldType);
      // 若旧类存在,调用 loadDirectoryInternal 加载旧路径下的配置文件,避免因类路径变更导致的兼容性问题。
      loadDirectoryInternal(extensionClasses, strategy, oldType);
    } catch (ClassNotFoundException classNotFoundException) {

    }
  }
}

org.apache.dubbo.common.extension.ExtensionLoader#loadDirectoryInternal

  • 参数

    • extensionClasses: 用于存储加载的扩展类的映射,键为扩展名,值为对应的 Class 对象

    • loadingStrategy: 加载策略,包含目录、是否覆盖、包含 / 排除的包等配置

    • type: 扩展点类型的名称

  • 用处

    • 多种类加载器的处理
    • 特殊 SPI 加载策略的定制
    • 范围模型类加载器的支持
    • 资源加载的错误处理
private void loadDirectoryInternal(
  Map<String, Class<?>> extensionClasses, LoadingStrategy loadingStrategy, String type)
  throws InterruptedException {
  // 构建文件名并初始化类加载器列表
  String fileName = loadingStrategy.directory() + type;
  try {
    List<ClassLoader> classLoadersToLoad = new LinkedList<>();

    // 优先处理扩展类加载器
    // 如果加载策略优先使用扩展类加载器,则将扩展加载器本身的类加载器加入列表
    if (loadingStrategy.preferExtensionClassLoader()) {
      ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
      if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
        classLoadersToLoad.add(extensionLoaderClassLoader);
      }
    }

    // 处理特殊 SPI 加载策略
    // 如果存在针对该类型的特殊加载策略,则检查当前加载策略名称是否匹配。若匹配则清空类加载器列表,仅使用扩展类加载器。
    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 {
      // 处理范围模型类加载器
      // 如果没有特殊加载策略,则获取范围模型的类加载器。若类加载器集合为空,则直接从系统类加载器加载资源;否则将这些类加载器添加到待处理列表。
      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);
      }
    }

    // 加载并处理资源
    // 使用类加载器资源加载器加载所有匹配的资源文件,然后遍历每个类加载器及其对应的资源 URL,调用 loadFromClass 方法处理这些资源。
    Map<ClassLoader, Set<java.net.URL>> resources =
      ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad);
    resources.forEach(((classLoader, urls) -> {
      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);
  }
}

网站公告

今日签到

点亮在社区的每一天
去签到