【Dubbo源码阅读系列】之 Dubbo SPI 机制

最近抽空开始了Dubbo源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对Dubbo的理解。如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出。DubboSPI介绍JavaSPI在阅读本文之前可能需要你对JavaSPI(ServiceProviderInterface)机制有过简单的了解。这里简单介绍下:在面向对象的设计中,我们提倡模块之间基于接口编程。不同模块可能会有不同的具体实现,但...

最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解。如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出。

Dubbo SPI 介绍

Java SPI

在阅读本文之前可能需要你对 Java SPI(Service Provider Interface) 机制有过简单的了解。这里简单介绍下:在面向对象的设计中,我们提倡模块之间基于接口编程。不同模块可能会有不同的具体实现,但是为了避免模块的之间的耦合过大,我们需要一种有效的服务(服务实现)发现机制来选择具体模块。SPI 就是这样一种基于接口编程 策略模式 配置文件,同时可供使用者根据自己的实际需要启用/替换模块具体实现的方案。

Dubbo SPI 的改进点

以下内容摘录自 https://dubbo.gitbooks.io/dubbo-dev-book/SPI.html
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点

在 Dubbo 中,如果某个 interface 接口标记了 @SPI 注解,那么我们认为它是 Dubbo 中的一个扩展点。扩展点是 Dubbo SPI 的核心,下面我们就扩展点加载、扩展点自动包装、扩展点自动装配几方面来聊聊具体实现。

Dubbo SPI 机制详解

Dubbo 扩展点的加载

在阅读本文前,如果你阅读过Java SPI 相关内容,大概能回忆起来有 /META-INF/services 这样一个目录。在这个目录下有一个以接口命名的文件,文件的内容为接口具体实现类的全限定名。在 Dubbo 中我们也能找到类似的设计。

  • META-INF/services/(兼容JAVA SPI)
  • META-INF/dubbo/(自定义扩展点实现)
  • META-INF/dubbo/internal/(Dubbo内部扩展点实现)

非常好~我们现在已经知道了从哪里加载扩展点了,再回忆一下,JAVA SPI是如何加载的。

ServiceLoader<DubboService> spiLoader = ServiceLoader.load(XXX.class);

类似的,在 Dubbo 中也有这样一个用于加载扩展点的类 ExtensionLoader。这一章节,我们会着重了解一下这个类到底是如何帮助我们加载扩展点的。我们先来看一段简短的代码。

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

在 Dubbo 的实现里面用到了大量类似的代码片段,我们只需要提供一个 type ,即可获取该 type 的自适应(关于自适应的理解在后文会提到)扩展类。在获取对应自适应扩展类时,我们首先获取该类型的 ExtensionLoader。看到这里我们应该下意识的感觉到对于每个 type 来说,都应该有一个对应的 ExtensionLoader 对象。我们先来看看 ExtensionLoader 是如何获取的。

getExtensionLoader()

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) {  throw new IllegalArgumentException(“Extension type == null“); } if (!type.isInterface()) {  throw new IllegalArgumentException(“Extension type(“type“) is not interface!“); } // 是否被 SPI 注解标识  if (!withExtensionAnnotation(type)) {  throw new IllegalArgumentException(“Extension type(“type   “) is not extension, because WITHOUT @“SPI.class.getSimpleName()“ Annotation!“); } //EXTENSION_LOADERS 为一个 ConcurrentMap集合,key 为 Class 对象,value 为ExtenLoader 对象 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) {  EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));  loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader;}private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());}

上面这一段的代码比较简单,根据 type 从 EXTENSION_LOADERS 集合中获取 loader ,如果返回的值为 null 则新建一个 ExtensionLoader 对象。这里的 objectFactory 获取也用到了类似的方法,获取到了 ExtensionFactory 的扩展自适应类。

getAdaptiveExtension()

public T getAdaptiveExtension() { //cachedAdaptiveInstance用于缓存自适应扩展类实例 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) {// ...  } }} } return (T) instance;}

getAdaptiveExtension()方法用于获取当前自适应扩展类实例,首先会从 cachedAdaptiveInstance 对象中获取,如果值为 null 同时 createAdaptiveInstanceError 为空,则调用 createAdaptiveExtension 方法创建扩展类实例。创建完后更新 cachedAdaptiveInstance 。

createAdaptiveExtension()

private T createAdaptiveExtension() { try {  return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) {  // 省略异常 }}

这里有两个方法值得我们关注,injectExtension() 和 getAdaptiveExtensionClass()。injectExtension() 看名字像是一个实现了注入功能的方法,而 getAdaptiveExtensionClass() 则用于获取具体的自适应扩展类。我们依次看下这两个方法。

injectExtension()

private T injectExtension(T instance) { try {  if (objectFactory != null) {for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith(“set“)&& method.getParameterTypes().length == 1&& Modifier.isPublic(method.getModifiers())) {  //如果存在 DisableInject 注解则跳过  if (method.getAnnotation(DisableInject.class) != null) {continue;  }  //获取 method 第一个参数的类型  Class<?> pt = method.getParameterTypes()[0];  try {String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase()method.getName().substring(4) : ““;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;}

简单的总结下这个方法做了什么:遍历当前实例的 set 方法,以 set 方法第四位开始至末尾的字符串为关键字,尝试通过 objectFactory 来获取对应的 扩展类实现。如果存在对应扩展类,通过反射注入到当前实例中。这个方法相当于完成了一个简单的依赖注入功能,我们常说 Dubbo 中的 IOC 实际上也是在这里体现的。

getAdaptiveExtensionClass()

private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) {  return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass();}

接着看 getAdaptiveExtensionClass() 方法。首先调用 getExtensionClasses() 方法,如果 cachedAdaptiveClass() 不为 null 则返回,如果为 null 则调用 createAdaptiveExtensionClass() 方法。依次看下这两个方法。

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;}

内容比较简单,我们直接看 loadExtensionClasses() 方法。

private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) {  String value = defaultAnnotation.value();  if ((value = value.trim()).length() > 0) {String[] names = NAME_SEPARATOR.split(value);// 只能有一
源文地址:https://www.guoxiongfei.cn/cntech/5533.html