摘要
Dubbo 为了更好地达到 OCP 原则(即“对扩展开放,对修改封闭”的原则),采用了“微内核+插件”的架构。那什么是微内核架构呢?微内核架构也被称为插件化架构(Plug-in Architecture),这是一种面向功能进行拆分的可扩展性架构。内核功能是比较稳定的,只负责管理插件的生命周期,不会因为系统功能的扩展而不断进行修改。功能上的扩展全部封装到插件之中,插件模块是独立存在的模块,包含特定的功能,能拓展内核系统的功能。微内核架构中,内核通常采用 Factory、IoC、OSGi 等方式管理插件生命周期,Dubbo 最终决定采用 SPI 机制来加载插件,Dubbo SPI 参考 JDK 原生的 SPI 机制,进行了性能优化以及功能增强。
一、SPI 机制原理
当服务的提供者提供了一种接口的实现之后,需要在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,此文件记录了该 jar 包提供的服务接口的具体实现类。当某个应用引入了该 jar 包且需要使用该服务时,JDK SPI 机制就可以通过查找这个 jar 包的 META-INF/services/ 中的配置文件来获得具体的实现类名,进行实现类的加载和实例化,最终使用该实现类完成业务功能。
package com.zhuangxiaoyan.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
/**
* @Classname Car
* @Description TODO
* @Date 2021/12/8 23:00
* @Created by xjl
*/
@SPI
public interface Car {
// @Adaptive
public void test(URL url);
}
package com.zhuangxiaoyan.dubbo;
import org.apache.dubbo.common.URL;
/**
* @Classname CarImpl
* @Description TODO
* @Date 2021/12/8 23:01
* @Created by xjl
*/
public class CarImpl implements Car {
@Override
public void test(URL url) {
System.out.println("SPI Test ……");
}
}
package com.zhuangxiaoyan.dubbo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.net.MalformedURLException;
/**
* @Classname SpiTest
* @Description TODO
* @Date 2021/12/8 23:00
* @Created by xjl
*/
public class SpiTest {
public static void main(String[] args) throws MalformedURLException {
ExtensionLoader<Car> extensionLoader= ExtensionLoader.getExtensionLoader(Car.class);
URL url=new URL("http","localhost", 8080);
Car car=extensionLoader.getExtension("car");
car.test(null);
}
}
car=com.zhuangxiaoyan.dubbo.CarImpl
二、JDK SPI 源码分析
我们可以看到 JDK SPI 的入口方法是 ServiceLoader.load() 方法,在 ServiceLoader.load() 方法中,首先会尝试获取当前使用的 ClassLoader(获取当前线程绑定的 ClassLoader,查找失败后使用 SystemClassLoader),然后调用 reload() 方法,调用关系如下图所示:
在 reload() 方法中,首先会清理 providers 缓存(LinkedHashMap 类型的集合),该缓存用来记录 ServiceLoader 创建的实现对象,其中 Key 为实现类的完整类名,Value 为实现类的对象。之后创建 LazyIterator 迭代器,用于读取 SPI 配置文件并实例化实现类对象。
ServiceLoader.reload() 方法的具体实现,如下所示:
// 缓存,用来缓存 ServiceLoader创建的实现对象
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
public void reload() {
providers.clear(); // 清空缓存
lookupIterator = new LazyIterator(service, loader); // 迭代器
}
在前面的示例中,main() 方法中使用的迭代器底层就是调用了 ServiceLoader.LazyIterator 实现的。Iterator 接口有两个关键方法:hasNext() 方法和 next() 方法。这里的 LazyIterator 中的next() 方法最终调用的是其 nextService() 方法,hasNext() 方法最终调用的是 hasNextService() 方法,调用关系如下图所示:
首先来看 LazyIterator.hasNextService() 方法,该方法主要负责查找 META-INF/services 目录下的 SPI 配置文件,并进行遍历,大致实现如下所示:
private static final String PREFIX = "META-INF/services/";
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
// PREFIX前缀与服务接口的名称拼接起来,就是META-INF目录下定义的SPI配
// 置文件(即示例中的META-INF/services/com.xxx.Log)
String fullName = PREFIX + service.getName();
// 加载配置文件
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
}
// 按行SPI遍历配置文件的内容
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
// 解析配置文件
pending = parse(service, configs.nextElement());
}
nextName = pending.next(); // 更新 nextName字段
return true;
}
在 hasNextService() 方法中完成 SPI 配置文件的解析之后,再来看 LazyIterator.nextService() 方法,该方法负责实例化 hasNextService() 方法读取到的实现类,其中会将实例化的对象放到 providers 集合中缓存起来,核心实现如下所示:
private S nextService() {
String cn = nextName;
nextName = null;
// 加载 nextName字段指定的类
Class<?> c = Class.forName(cn, false, loader);
if (!service.isAssignableFrom(c)) { // 检测类型
fail(service, "Provider " + cn + " not a subtype");
}
S p = service.cast(c.newInstance()); // 创建实现类的对象
providers.put(cn, p); // 将实现类名称以及相应实例对象添加到缓存
return p;
}
以上就是在 main() 方法中使用的迭代器的底层实现。最后,我们再来看一下 main() 方法中使用ServiceLoader.iterator() 方法拿到的迭代器是如何实现的,这个迭代器是依赖 LazyIterator 实现的一个匿名内部类,核心实现如下:
public Iterator<S> iterator() {
return new Iterator<S>() {
// knownProviders用来迭代providers缓存
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
// 先走查询缓存,缓存查询失败,再通过LazyIterator加载
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
public S next() {
// 先走查询缓存,缓存查询失败,再通过 LazyIterator加载
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next();
}
// 省略remove()方法
};
}
三、Dubbo SPI机制原理
- 扩展点:通过 SPI 机制查找并加载实现的接口(又称“扩展接口”)。前文示例中介绍的 Log 接口、com.mysql.cj.jdbc.Driver 接口,都是扩展点。
- 扩展点实现:实现了扩展接口的实现类。
JDK SPI 在查找扩展实现类的过程中,需要遍历 SPI 配置文件中定义的所有实现类,该过程中会将这些实现类全部实例化。如果 SPI 配置文件中定义了多个实现类,而我们只需要使用其中一个实现类时,就会生成不必要的对象。例如,org.apache.dubbo.rpc.Protocol 接口有 InjvmProtocol、DubboProtocol、RmiProtocol、HttpProtocol、HessianProtocol、ThriftProtocol 等多个实现,如果使用 JDK SPI,就会加载全部实现类,导致资源的浪费。
Dubbo SPI 不仅解决了上述资源浪费的问题,还对 SPI 配置文件扩展和修改。
首先,Dubbo 按照 SPI 配置文件的用途,将其分成了三类目录。
- META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI 。
- META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件。
- META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件。
然后,Dubbo 将 SPI 配置文件改成了 KV 格式,例如:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
其中 key 被称为扩展名(也就是 ExtensionName),当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现。例如,这里指定扩展名为 dubbo,Dubbo SPI 就知道我们要使用:org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类。
使用 KV 格式的 SPI 配置文件的另一个好处是:让我们更容易定位到问题。假设我们使用的一个扩展实现类所在的 jar 包没有引入到项目中,那么 Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载。这些更加准确的异常信息降低了排查问题的难度,提高了排查问题的效率。
四、Dubbo SPI 源码分析
Dubbo 中某个接口被 @SPI注解修饰时,就表示该接口是扩展接口,前文示例中的 org.apache.dubbo.rpc.Protocol 接口就是一个扩展接口:
@SPI 注解的 value 值指定了默认的扩展名称,例如,在通过 Dubbo SPI 加载 Protocol 接口实现时,如果没有明确指定扩展名,则默认会将 @SPI 注解的 value 值作为扩展名,即加载 dubbo 这个扩展名对应的 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类,相关的 SPI 配置文件在 dubbo-rpc-dubbo 模块中,如下图所示:
4.1 ExtensionLoader源码
该类是扩展加载器,这是dubbo实现SPI扩展机制等核心,几乎所有实现的逻辑都被封装在ExtensionLoader中。
1关于存放配置文件的路径变量:
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
"META-INF/services/"、"META-INF/dubbo/"、"META-INF/dubbo/internal/"三个值,都是dubbo寻找扩展实现类的配置文件存放路径,也就是我在上述(一)注解@SPI中讲到的以接口全限定名命名的配置文件存放的路径。区别在于"META-INF/services/"是dubbo为了兼容jdk的SPI扩展机制思想而设存在的,"META-INF/dubbo/internal/"是dubbo内部提供的扩展的配置文件路径,而"META-INF/dubbo/"是为了给用户自定义的扩展实现配置文件存放。
2扩展加载器集合,key为扩展接口,例如Protocol等:
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
3扩展实现类集合,key为扩展实现类,value为扩展对象,例如key为
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
4以下属性都是cache开头的,都是出于性能和资源的优化,才做的缓存,读取扩展配置后,会先进行缓存,等到真正需要用到某个实现时,再对该实现类的对象进行初始化,然后对该对象也进行缓存。
//以下提到的扩展名就是在配置文件中的key值,类似于“dubbo”等
//缓存的扩展名与拓展类映射,和cachedClasses的key和value对换。
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
//缓存的扩展实现类集合
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
//扩展名与加有@Activate的自动激活类的映射
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//缓存的扩展对象集合,key为扩展名,value为扩展对象
//例如Protocol扩展,key为dubbo,value为DubboProcotol
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object
//缓存的自适应( Adaptive )扩展对象,例如例如AdaptiveExtensionFactory类的对象
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//缓存的自适应扩展对象的类,例如AdaptiveExtensionFactory类
private volatile Class<?> cachedAdaptiveClass = null;
//缓存的默认扩展名,就是@SPI中设置的值
private String cachedDefaultName;
//创建cachedAdaptiveInstance异常
private volatile Throwable createAdaptiveInstanceError;
//拓展Wrapper实现类集合
private Set<Class<?>> cachedWrapperClasses;
//拓展名与加载对应拓展类发生的异常的映射
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
这里提到了Wrapper类的概念。那我就解释一下:Wrapper类也实现了扩展接口,但是Wrapper类的用途是ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外,这实现了扩展点自动包装的特性。通俗点说,就是一个接口有很多的实现类,这些实现类会有一些公共的逻辑,如果在每个实现类写一遍这个公共逻辑,那么代码就会重复,所以增加了这个Wrapper类来包装,把公共逻辑写到Wrapper类中,有点类似AOP切面编程思想。
4.2 getExtensionLoader源码
根据扩展点接口来获得扩展加载器。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//扩展点接口为空,抛出异常
if (type == null)
throw new IllegalArgumentException("Extension type == null");
//判断type是否是一个接口类
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//判断是否为可扩展的接口
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//从扩展加载器集合中取出扩展接口对应的扩展加载器
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//如果为空,则创建该扩展接口的扩展加载器,并且添加到EXTENSION_LOADERS
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
4.3 getActivateExtension源码
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
//弃用
public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
// 获得符合自动激活条件的拓展对象数组
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
//判断不存在配置 `"-name"` 。
//例如,<dubbo:service filter="-default" /> ,代表移除所有默认过滤器。
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
//获得扩展实现类数组,把扩展实现类放到cachedClasses中
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
//判断group值是否存在所有自动激活类中group组中,匹配分组
if (isMatchGroup(group, activate.group())) {
//通过扩展名获得拓展对象
T ext = getExtension(name);
//不包含在自定义配置里。如果包含,会在下面的代码处理。
//判断是否配置移除。例如 <dubbo:service filter="-monitor" />,则 MonitorFilter 会被移除
//判断是否激活
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
//排序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
//还是判断是否是被移除的配置
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
//在配置中把自定义的配置放在自动激活的扩展对象前面,可以让自定义的配置先加载
//例如,<dubbo:service filter="demo,default,demo2" /> ,则 DemoFilter 就会放在默认的过滤器前面。
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
可以看到getActivateExtension重载了四个方法,其实最终的实现都是在最后一个重载方法,因为自动激活类的条件可以分为无条件、只有value以及有group和value三种。
4.4 getExtension源码
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//查找默认的扩展实现,也就是@SPI中的默认值作为key
if ("true".equals(name)) {
return getDefaultExtension();
}
//缓存中获取对应的扩展对象
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//通过扩展名创建接口实现类的对象
instance = createExtension(name);
//把创建的扩展对象放入缓存
holder.set(instance);
}
}
}
return (T) instance;
}
4.5 getDefaultExtension源码
查找默认的扩展实现
public T getDefaultExtension() {
//获得扩展接口的实现类数组
getExtensionClasses();
if (null == cachedDefaultName || cachedDefaultName.length() == 0
|| "true".equals(cachedDefaultName)) {
return null;
}
//又重新去调用了getExtension
return getExtension(cachedDefaultName);
}
4.6 addExtension源码
public void addExtension(String name, Class<?> clazz) {
getExtensionClasses(); // load classes
//该类是否是接口的本身或子类
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Input type " +
clazz + "not implement Extension " + type);
}
//该类是否被激活
if (clazz.isInterface()) {
throw new IllegalStateException("Input type " +
clazz + "can not be interface!");
}
//判断是否为适配器
if (!clazz.isAnnotationPresent(Adaptive.class)) {
if (StringUtils.isBlank(name)) {
throw new IllegalStateException("Extension name is blank (Extension " + type + ")!");
}
if (cachedClasses.get().containsKey(name)) {
throw new IllegalStateException("Extension name " +
name + " already existed(Extension " + type + ")!");
}
//把扩展名和扩展接口的实现类放入缓存
cachedNames.put(clazz, name);
cachedClasses.get().put(name, clazz);
} else {
if (cachedAdaptiveClass != null) {
throw new IllegalStateException("Adaptive Extension already existed(Extension " + type + ")!");
}
cachedAdaptiveClass = clazz;
}
}
4.7 getAdaptiveExtension源码
获得自适应扩展对象,也就是接口的适配器对象,思路就是先从缓存中取适配器类的对象,如果没有,则创建一个适配器对象,然后放入缓存,createAdaptiveExtension方法解释在后面给出
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//创建适配器对象
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
4.8 createExtension源码
通过扩展名创建扩展接口实现类的对象。
@SuppressWarnings("unchecked")
private T createExtension(String name) {
//获得扩展名对应的扩展实现类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
//看缓存中是否有该类的对象
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//向对象中注入依赖的属性(自动装配)
injectExtension(instance);
//创建 Wrapper 扩展对象(自动包装)
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
4.9 injectExtension源码
向创建的拓展注入其依赖的属性,思路就是是先通过反射获得类中的所有方法,然后找到set方法,找到需要依赖注入的属性,然后把对象注入进去。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
//反射获得该类中所有的方法
for (Method method : instance.getClass().getMethods()) {
//如果是set方法
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
//获得属性,比如StubProxyFactoryWrapper类中有Protocol protocol属性,
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
//获得属性值,比如Protocol对象,也可能是Bean对象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//注入依赖属性
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
4.10 getExtensionClass源码
获得扩展名对应的扩展实现类
private Class<?> getExtensionClass(String name) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (name == null)
throw new IllegalArgumentException("Extension name == null");
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null)
throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!");
return clazz;
}
4.11 getExtensionClasses源码
获得扩展实现类数组,这里思路就是先从缓存中取,如果缓存为空,则从配置文件中读取扩展实现类。
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//从配置文件中,加载扩展实现类数组
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
4.12 loadExtensionClasses源码
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//@SPI内的默认值
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
//只允许有一个默认值
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
//从配置文件中加载实现类数组
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
前一部分逻辑是在把SPI注解中的默认值放到缓存中去,加载实现类数组的逻辑是在后面几行,关键的就是loadDirectory方法。并且这里可以看出去找配置文件访问的资源路径顺序。
4.13 loadDirectory源码
从一个配置文件中,加载拓展实现类数组,这边的思路是先获得完整的文件名,遍历每一个文件,在loadResource方法中去加载每个文件的内容。
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
//拼接接口全限定名,得到完整的文件名
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
//获取ExtensionLoader类信息
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//遍历文件
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
4.14 loadResource源码
加载文件中的内容,该类的主要的逻辑就是读取里面的内容,跳过“#”注释的内容,根据配置文件中的key=value的形式去分割,然后去加载value对应的类。
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
//跳过被#注释的内容
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
//根据"="拆分key跟value
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//加载扩展类
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} 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);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
4.15 loadClass源码
根据配置文件中的value加载扩展类,重点关注该方法中兼容了jdk的SPI思想。因为jdk的SPI相关的配置文件中是xx.yyy.DemoFilter,并没有key,也就是没有扩展名的概念,所有为了兼容,通过xx.yyy.DemoFilter生成的扩展名为demo。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//该类是否实现扩展接口
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
}
//判断该类是否为扩展接口的适配器
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
}
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//通过反射获得构造器对象
clazz.getConstructor();
//未配置扩展名,自动生成,例如DemoFilter为 demo,主要用于兼容java SPI的配置。
if (name == null || name.length() == 0) {
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 (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
//如果是自动激活的实现类,则加入到缓存
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
//缓存扩展实现类
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
4.16 createAdaptiveExtensionClass源码
创建适配器类,类似于dubbo动态生成的Transporter$Adpative这样的类。
private Class<?> createAdaptiveExtensionClass() {
//创建动态生成的适配器类代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//编译代码,返回该类
return compiler.compile(code, classLoader);
}
这个方法中就做了编译代码的逻辑,生成代码在createAdaptiveExtensionClassCode方法中,createAdaptiveExtensionClassCode方法由于过长,我不在这边列出,下面会给出github的网址,读者可自行查看相关的源码解析。createAdaptiveExtensionClassCode生成的代码逻辑可以对照上述讲的注解@Adaptive中的Transporter$Adpative类来看。
4.17 AdaptiveExtensionFactory源码
该类是ExtensionFactory的适配器类
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
//扩展对象的集合,默认的可以分为dubbo 的SPI中接口实现类对象或者Spring bean对象
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
//遍历所有支持的扩展名
for (String name : loader.getSupportedExtensions()) {
//扩展对象加入到集合中
list.add(loader.getExtension(name));
}
//返回一个不可修改的集合
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
//通过扩展接口和扩展名获得扩展对象
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
- factories是扩展对象的集合,当用户没有自己实现ExtensionFactory接口,则这个属性就只会有两种对象,分别是 SpiExtensionFactory 和 SpringExtensionFactory 。
- 构造器中是把所有支持的扩展名的扩展对象加入到集合
- 实现了接口的getExtension方法,通过接口和扩展名来获取扩展对象。
4.18 SpiExtensionFactory源码
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
//判断是否为接口,接口上是否有@SPI注解
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
//获得扩展加载器
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
//返回适配器类的对象
return loader.getAdaptiveExtension();
}
}
return null;
}
}
4.19 ActivateComparator源码
该类在ExtensionLoader类的getActivateExtension方法中被运用到,作为自动激活拓展对象的排序器。
public class ActivateComparator implements Comparator<Object> {
public static final Comparator<Object> COMPARATOR = new ActivateComparator();
@Override
public int compare(Object o1, Object o2) {
//基本排序
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.equals(o2)) {
return 0;
}
Activate a1 = o1.getClass().getAnnotation(Activate.class);
Activate a2 = o2.getClass().getAnnotation(Activate.class);
//使用Activate注解的 `after` 和 `before` 属性,排序
if ((a1.before().length > 0 || a1.after().length > 0
|| a2.before().length > 0 || a2.after().length > 0)
&& o1.getClass().getInterfaces().length > 0
&& o1.getClass().getInterfaces()[0].isAnnotationPresent(SPI.class)) {
ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(o1.getClass().getInterfaces()[0]);
if (a1.before().length > 0 || a1.after().length > 0) {
String n2 = extensionLoader.getExtensionName(o2.getClass());
for (String before : a1.before()) {
if (before.equals(n2)) {
return -1;
}
}
for (String after : a1.after()) {
if (after.equals(n2)) {
return 1;
}
}
}
if (a2.before().length > 0 || a2.after().length > 0) {
String n1 = extensionLoader.getExtensionName(o1.getClass());
for (String before : a2.before()) {
if (before.equals(n1)) {
return 1;
}
}
for (String after : a2.after()) {
if (after.equals(n1)) {
return -1;
}
}
}
}
// 使用Activate注解的 `order` 属性,排序。
int n1 = a1 == null ? 0 : a1.order();
int n2 = a2 == null ? 0 : a2.order();
// never return 0 even if n1 equals n2, otherwise, o1 and o2 will override each other in collection like HashSet
return n1 > n2 ? 1 : -1;
}
}
博文参考
深入理解Apache Dubbo与实战.pdf
面试官问烂的Dubbo中SPI机制的源码解析_哔哩哔哩_bilibili
标签:Dubbo,name,扩展,SPI,String,new,null,加载 From: https://blog.51cto.com/u_13643065/6169226