Dubbo源码分析DubboSPI通过getExtension获取扩展点实现类对象

编程

开篇

前面用了4 篇文章分析了 Dubbo SPI 的几种用法以及如何在 Dubbo 中应用的,

  • Dubbo SPI 使用方法(一)- 扩展点自动包装
  • Dubbo SPI 使用方法(二)- 扩展点自适应
  • Dubbo SPI 使用方法(三)- 扩展点自动装配
  • Dubbo 源码分析 - Dubbo SPI 在 Protocol 层 的应用

本文通过调试 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源码分析DubboSPI通过getExtension获取扩展点实现类对象 的全部内容, 来源链接: utcz.com/z/514851.html

回到顶部