DubboSPI
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/)读取配置文件,并将扩展名与实现类的映射存入 extensionClassesloadDirectoryInternal(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 matchif (!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);}
}