dubbo源码阅读之-dubbo-spi机制中的配置文件都可以存放再哪个路径下
url图
根据上图可以知道 存放的路径和优先级,我们也可以自定义一个路径
ExtensionLoader 类中
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private static LoadingStrategy[] loadLoadingStrategies() {
return stream(load(LoadingStrategy.class).spliterator(), false).sorted()
.toArray(LoadingStrategy[]::new);
}
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
可以看出来是用的java原生的ServiceLoader 加载所有的加载策略,最后存储到一个数组中
默认加载策略有
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy
org.apache.dubbo.common.extension.DubboLoadingStrategy
org.apache.dubbo.common.extension.ServicesLoadingStrategy
加载源码分析
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses 入口
loadExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
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);
}
}
}
return classes;
}
上面方法就是包装了一层缓存而已
缓存包装
classes = loadExtensionClasses();
/**
* synchronized in getExtensionClasses
* 根据策略下载不同扩展类
*/
@SuppressWarnings("deprecation")
private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
checkDestroyed();
/**
* 缓存默认的扩展名到成员变量cachedDefaultName中
* 1. @SPI(value="aa") 默认值只能写一个,不能写aa,bb 这样带有逗号的字符串
* 2. aa必须是存在的,否则获取默认实现报错
*/
cacheDefaultExtensionName();
//加载到的扩展集合
Map<String, Class<?>> extensionClasses = new HashMap<>();
/**
* 扩展策略,在前面章节中我们介绍了这个类型的UML与说明
* LoadingStrategy扩展加载策略,目前有3个扩展加载策略
* DubboInternalLoadingStrategy:Dubbo内置的扩展加载策略,将加载文件目录为META-INF/dubbo/internal/的扩展
* DubboLoadingStrategy:Dubbo普通的扩展加载策略,将加载目录为META-INF/dubbo/的扩展
* ServicesLoadingStrategy:JAVA SPI加载策略 ,将加载目录为META-INF/services/的扩展
* 扩展策略集合对象在什么时候初始化的呢在成员变量初始化的时候就创建了集合对象,
* 这个可以看方法loadLoadingStrategies() 通过Java的 SPI加载策略
**/
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy, type.getName());
// compatible with old ExtensionFactory
//如果当前要加载的扩展类型是扩展注入类型则扫描下ExtensionFactory类型的扩展
if (this.type == ExtensionInjector.class) {
//这个方法和上面那个方法是一样的就不详细说了 扫描文件 找到扩展类型
loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
}
}
/**
*通过loadDirectory扫描 扫描到了ExtensionInjector类型的扩展实现类有3个 我们将会得到这样一个集合例子:
*"spring" -> "class org.apache.dubbo.config.spring.extension.SpringExtensionInjector"
*"scopeBean" -> "class org.apache.dubbo.common.beans.ScopeBeanExtensionInjector"
*"spi" -> "class org.apache.dubbo.common.extension.inject.SpiExtensionInjector"
**/
return extensionClasses;
}
总结:1.默认name缓存:cachedDefaultName 只能写一个name,如果用逗号分开写的跑异常
2. 根据加载策略分别获取配置文件中的内容目前支持:META-INF/dubbo/internal/ ,META-INF/dubbo/ ,META-INF/services/ 当然也可以自定义
3. 如果是ExtensionInjector 类型加载ExtesionFactory类
每个策略加载一遍
不同的扩展策略传递了不同的参数,但是扩展的加载流程是相同的
不同的内容有:
- 路径不同
- 类加载器可能不同配置信息
//不同的扩展策略传递了不同的参数,但是扩展的加载流程是相同的,这里我们可以参考上面表格
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);
}
}
//special_spi.properties 是否包含 该type 这个是为了优化 dubbo 框架扩展 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 {
// 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);
}
}
/上半部分为策略处理ClassLoader
//使用类加载资源加载器(ClassLoaderResourceLoader)来加载具体的资源
Map<ClassLoader, Set<java.net.URL>> resources = ClassLoaderResourceLoader.loadResources(
fileName, classLoadersToLoad);
//遍历从所有资源文件中读取到资源url地址,key为类加载器,值为扩展文件url如夏所示
//jar:file:/Users/xxx/.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) -> {
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);
}
}
每个ClaasLoader加载扩展点
loadFromClass
//加载url并放到缓存中
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);
}
}
}
每个Url 加载
每个URL加载资源我们是知道真正的获取内容
// 加载资源
//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
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;
}
//isExcluded是否为加载策略要排除的配置,参数这里为空代表全部类型不排除
//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);
}
}
加载实现类
根据我们配置的
red,red1,red2=com.wfg.dubbo.demo.spi.RedCar
back=com.wfg.dubbo.demo.spi.BackCar
com.wfg.dubbo.demo.spi.CarWrapper
进行加载RedCar
BackCar
CarWrapper
//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.");
}
//扩展子类型是否存在这个注解@Adaptive
boolean isActive = loadClassIfActive(classLoader, clazz);
if (!isActive) {
return;
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);
} else if (isWrapperClass(clazz)) {
//扩展子类型构造器中是否有这个类型的接口 (这个可以想象下我们了解的Java IO流中的类型使用到的装饰器模式 构造器传个类型)
cacheWrapperClass(clazz);
} else {
//无自适应注解,也没有构造器是扩展类型参数 ,这个name我们在扩展文件中找到了就是等号前面那个
if (StringUtils.isEmpty(name)) {
//低版本中可以使用@Extension 扩展注解来标注扩展类型,这里获取注解有两个渠道:
//先查询@Extension注解是否存在如果存在则取value值,如果不存在@Extension注解则获取当前类型的名字
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);
//@Activate注解修饰的扩展
if (ArrayUtils.isNotEmpty(names)) {
//cachedNames缓存集合缓存当前扩展类型的扩展名字
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
//将扩展类型加入结果集合extensionClasses中,不允许覆盖的话出现同同名字扩展将抛出异常
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}