概述
开篇
前面用了4 篇文章分析了 Dubbo SPI 的几种用法以及如何在 Dubbo 中应用的,
- Dubbo SPI 使用方法(一)- 扩展点自动包装
- Dubbo SPI 使用方法(二)- 扩展点自适应
- Dubbo SPI 使用方法(三)- 扩展点自动装配
- Dubbo 源码分析 - Dubbo SPI 在 Protocol 层 的应用
本文通过 Debug Dubbo2.7.x 源码的方法分析 如何通过 getExtension(name) 获取一个扩展对象实例 。
正文
回顾一下 Dubbo SPI 的最基本的用法
public class App
{
public static void main( String[] args )
{
// 第一步
ExtensionLoader<HelloService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloService.class);
// 第二步
HelloService helloService = extensionLoader.getExtension("helloService");
// 第三步
helloService.sayHello("xiaoming");
}
}
我们直接从第二步 debug 进入getExtension(name)
方法
1. ExtensionLoader 的属性
在 debug 进入方法之前, 先来看几个 ExtensionLoader
的属性
// 1. 扩展接口, 比如 Protocol
private final Class<?> type;
// 2. 扩展实现类集合, key 为 Protocol , value 为 DubboProtocol
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
// 3. (缓存的)扩展实现类集合
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
// 4. 缓存的扩展对象集合 key 为 dubbo, value 为 DubboProtocol
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 5. 扩展增强 Wrapper 实现类集合
private Set<Class<?>> cachedWrapperClasses;
2. ExtensionLoader # getExtension(String name)
此方法大致分为 3 大步:
- 如果 name 为 “true”, 则获取 默认的扩展类对象
- 否则, 就去取 缓存的扩展类对象
- 如果缓存中不存在,就去加载并 实例化扩展类 , 并放入缓存
下面就针对上面的 三大步, 逐个分析
public T getExtension(String name) {
// 省略扩展名非空校验
// 1. 如果 name 等于 true, 获取默认的扩展对象
if ("true".equals(name)) {
return getDefaultExtension();
}
// 2. 从缓存的扩展类对象获取
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
// 缓存中没有对应的实例
if (instance == null) {
synchronized (holder) {
instance = holder.get();
// 双重校验
if (instance == null) {
// 3. 加载扩展实现类,并实例化
instance = createExtension(name);
// 扩展对象放入缓存
holder.set(instance);
}
}
}
return (T) instance;
}
2.1. ExtensionLoader # getDefaultExtension()
public T getDefaultExtension() {
// 1.1 获取所有的扩展类
getExtensionClasses();
if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
return null;
}
return getExtension(cachedDefaultName);
}
2.1.1 getExtensionClasses()
private Map<String, Class<?>> getExtensionClasses() {
// 先从缓存中取
// cachedClasses 放置的是缓存的扩展实现类集合
Map<String, Class<?>> classes = cachedClasses.get();
// 依然是双重校验+锁的方法去获取
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 缓存没有命中, 就会去加载 META-INF/dubbo ,META-INF/dubbo/intenal, ,META-INF/service 目录下去加载
classes = loadExtensionClasses();
// 加载完成,就放入缓存
cachedClasses.set(classes);
}
}
}
return classes;
}
2.1.2 loadExtensionClasses()
上面也有提到, 缓存中没有,就会去下面的目录去加载文件, 然后解析文件中的内容
- META-INF/dubbo
- META-INF/dubbo/intenal
- META-INF/service
具体源码就不贴出来了, 文件的内容格式如下:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
2.2. ExtensionLoader # getOrCreateHolder()
private Holder<Object> getOrCreateHolder(String name) {
// 跟上面如出一辙, 先从缓存中取,
// cachedInstances 放置的是 缓存的扩展类对象
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<>());
holder = cachedInstances.get(name);
}
return holder;
}
2.3 ExtensionLoader # createExtension(String name)
- 根据名称(比如: dubbo)获取扩展实现类(
DubboProtocol
) - 从扩展实现类集合中获取扩展实现类对象
instance
- 如果扩展类对象
instance
依赖其他扩展实现类 OtherClass, 就需要把 OtherClass 实例化,并通过 setter 方法注入到instance
里面 - 判断 Protocol 是否有其他增强实现, 比如
ProtocolFilterWrapper
等等- 如果有, 则把它
ProtocolFilterWrapper
实例化, 赋值给instance
- 如果有, 则把它
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 (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
// 重新赋值成一个 wrapper (增强类)
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
总结
本文主要针对通过 getExtension(name)
获取一个扩展对象实例, 来对 Dubbo 的源码进行了剖析, 当然全文只是描述了一个大概的流程, 比如如何解析 SPI 配置文件 就没有深入去讲解.
再熟悉下大体流程
- 如果是默认的扩展对象(
"true".equals(name)
), 通过getDefaultExtension()
方法获取 默认的扩展接口实现类对象, 并返回 - 判断缓存
cachedInstances
集合中是否存在- 如果有, 就从缓存的扩展接口实现类对象获取, 赋值给
instance
- 如果没有, 就通过
createExtension
获取 扩展接口实现类对象- 获取扩展实现类 比如
DubboProtocol
- 判断缓存中是否有扩展接口实现类的对象
instance
, 如果没有就把上一步的类DubboProtocol
给实例化 - 判断
instance
是否依赖其他扩展接口实现类对象CLassA, CLassB
等等,如果有,需要通过 setter 方法注入进去 - 判断
DubboProtocol
是否有其他增 Wrapper 实现类, 比如ProtocolFilterWrapper
, 如果有, 赋值给上面的instance
- 最后放入缓存
- 获取扩展实现类 比如
- 如果有, 就从缓存的扩展接口实现类对象获取, 赋值给
- 返回扩展实现类对象
return instance
下一篇文章会针对 获取自适应扩展点实例 getAdaptiveExtension()
方法进行源码分析!
最后
以上就是魔幻宝贝为你收集整理的Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象的全部内容,希望文章能够帮你解决Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复