为什么80%的码农都做不了架构师?>>>   

从dubbo的SPI理念开始,由于dubbo的项目Leader一开始就把其定义成一个方便扩展的服务框架,所以在dubbo的架构设计中始终保持了良好的依赖扩展机制:微内核+插件。简而言之就是让扩展者可以和项目开发者拥有一样的灵活度,这也是dubbo得以迅速流行的一个必要条件。

要想实现这种自由度,除了在架构分层组件上要保持高内聚低耦合外,底层也需要一套强大的类管理工具。在javaEE世界里,把这份工作做到极致的也已经有成熟的标准规范:OSGi。不过OSGi并不完全适配dubbo的需求,而且这玩意儿也有些过于重了,所以在此基础上,dubbo结合JDK标准的SPI机制设计出来一个轻量级的实现:Cooma。

这篇文章,我就打算从Cooma说起,官方介绍的已经非常详细了,不过它在从dubbo独立出来发布之前是做过修改优化的,在dubbo项目中使用时可能会存在些许的不同,我们就从dubbo内部来研读这部分实现的代码,并结合dubbo中的上下文来了解一下dubbo是如何使用SPI的。

我们把目标定位在dubbo的这个包上:

com.alibaba.dubbo.common.extension

看一下这个包的目录结构:

com.alibaba.dubbo.common.extension||--factory|     |--AdaptiveExtensionFactory   #稍后解释|     |--SpiExtensionFactory        #稍后解释||--support|     |--ActivateComparator||--Activate  #自动激活加载扩展的注解|--Adaptive  #自适应扩展点的注解|--ExtensionFactory  #扩展点对象生成工厂接口|--ExtensionLoader   #扩展点加载器,扩展点的查找,校验,加载等核心逻辑的实现类|--SPI   #扩展点注解

我们通过对照dubbo如何使用扩展点机制来完成扩展点工厂实例的选择与加载来了解一下扩展点实现的细节,这句话很拗口,有点递归的味道,我们不妨直接从代码中来理解:

public class ExtensionLoader<T> {...private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();private final Class<?> type;        ...@SuppressWarnings("unchecked")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!");}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);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());}        ...
}

我们主要看ExtensionLoader构造方法,其中它初始化了typeobjectFactory,前者为要作为扩展点的接口类型,后者表示要如何获取指定名称的扩展点实例(工厂类),目前dubbo提供了2个实现类,上面在包结构图上已经标注过了。

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

上面这个方法是用来获取自适应扩展类实例的,但其实它只是封装了一层缓存而已,真正完成创建实例的是createAdaptiveExtension方法。由于调用关系太深,请看下面的图:

上图中给出的路径,缺少了查找扩展点实现的细节,也就是并没有展开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方法:

// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {//检查并获取该接口类型声明的默认扩展点实现final SPI defaultAnnotation = type.getAnnotation(SPI.class);if(defaultAnnotation != null) {String value = defaultAnnotation.value();if(value != null && (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<?>>();loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);loadFile(extensionClasses, DUBBO_DIRECTORY);loadFile(extensionClasses, SERVICES_DIRECTORY);return extensionClasses;
}

我们先来看一下配置文件的格式:

adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory

loadFile方法会从指定位置(META-INF/dubbo/internal/)根据指定接口类型(type)为文件名称查找目标配置文件,然后解析并校验,最终拿到匹配的扩展点类的所有Class实例。对应上面给出的配置文件,也就是AdaptiveExtensionFactorySpiExtensionFactory,它们已经在包结构图上提到过了。

现在我们来着重看一下这两个类,它们到底是做什么用的呢?首先,AdaptiveExtensionFactory定义上有@Adaptive注解标识,很明显,它就是自适应扩展点的实现,从loadFile方法中可以留意到:同一个接口类型只能存在一个自适应扩展点实现

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {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);}//从这个方法定义来看,这个自适应扩展点实现类并没有做任何事儿,唯一的工作就是把真正获取扩展点实例的逻辑依次交给//框架中声明的所有ExtensionFactory扩展点实例,默认也就是SpiExtensionFactorypublic <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;}
}

可以看到,AdaptiveExtensionFactory把逻辑委托给SpiExtensionFactory来做,而后者又是怎么做的呢:

public class SpiExtensionFactory implements ExtensionFactory {public <T> T getExtension(Class<T> type, String name) {if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);if (loader.getSupportedExtensions().size() > 0) {//获取自适应扩展点实例,这是dubbo默认的行为,//也可以自己写一个ExtensionFactory来按照要求加载扩展点return loader.getAdaptiveExtension();   }}return null;}
}

objectFactory(真实工作的也就是SpiExtensionFactory.getExtension)只是用在ExtendLoader的注入方法(injectExtension)中,该方法用于为选定的扩展点实现注入相关的其他扩展点实例。

转载于:https://my.oschina.net/oosc/blog/1791178

Dubbo SPI的基础Cooma微容器相关推荐

  1. JDK、Spring、Dubbo SPI 原理介绍

    导读: 需求变化是程序员生命中唯一不变的事情,本文将介绍 JDK/Spring/Dubbo 中的 SPI 机制,以此来帮助我们编写出一套可扩展性强,易于维护的代码框架. 文|杨亮 网易云商高级 Jav ...

  2. Dubbo 如何成为连接异构微服务体系的最佳服务开发框架

    来自:阿里巴巴中间件 Photo @ Ilya Orehov 文 |刘军 从编程开发的角度来说,Apache Dubbo (以下简称 Dubbo)首先是一款 RPC 服务框架,它最大的优势在于提供了面 ...

  3. dubbo consumer 端口_基于Springboot+Dubbo+Nacos 注解方式实现微服务调用

    今天跟大家分享基于Springboot+Dubbo+Nacos 注解方式实现微服务调用的知识. 1 项目结构 |-- spring-boot-dubbo-demo (父级工程) |-- spring- ...

  4. Dubbo源码分析系列-深入Dubbo SPI机制

    导语   在之前的博客中介绍过关于Java中SPI的机制,也简单的分析了关于Java中SPI怎么去使用.SPI的全称Service Provider Interface,是一种服务发现机制.SPI的本 ...

  5. Dubbo 2.7.3源码分析——Dubbo SPI(二)

    老板,救救孩子?关注一下吧! 作者 肥又君 前情回顾 在前面介绍了Dubbo SPI 中 ExtensionLoader 的基础使用和原理,本篇重点介绍 Dubbo SPI 对 @Adaptive 注 ...

  6. Dubbo SPI机制和原理解析

    简介 SPI(service provider interface)是一种服务发现机制,通过加载指定路径下配置文件中的实现类,达到运行时用实现动态替换接口的目的.SPI常常用于扩展应用的功能,Dubb ...

  7. 基础篇--Web容器学习路径

    在开篇词里我提到要成长为一名高级程序员或者架构师,我们需要提高自己知识的广度和深度.你可以先突破深度,再以点带面拓展广度,因此我建议通过深入学习一些优秀的开源系统来达到突破深度的目的. 我会跟你一起在 ...

  8. Dubbo SPI机制(上):一个普通的扩展类是如何加载的

    这一篇我们先不讲Dubbo中的具体业务逻辑,我们来打基础,聊一聊Dubbo中的SPI机制. Dubbo SPI是干啥的 了解一个技术,得先知道它是为了解决什么问题而产生的.那么Dubbo SPI是干什 ...

  9. 微处理器硬件喂狗_硬件基础:微控制器到底是什么?

    微处理器硬件喂狗 by Taron Foxworth 通过塔伦·福克斯沃思(Taron Foxworth) 硬件基础:微控制器到底是什么? (Hardware Fundamentals: what e ...

最新文章

  1. 分布式链路追踪zipkin
  2. vue-cli安装笔记
  3. 最近看的电影(乱谈)
  4. Matlab之rand(), randn(), randi()函数的使用方法
  5. 搜索——二分搜索实现及细节
  6. 实例教你怎么使用s扫描器
  7. mysql查询名字重复四次以上的人名_怎么查询数据库中重复字段的名字
  8. 在线制作banner
  9. 鸿蒙app前后端流程实现
  10. 运动电荷的电磁场(一)
  11. java微信公众号素材管理系统_微信公众平台后台素材管理
  12. 【编程学习】浅谈哈希表及用C语言构建哈希表!
  13. Eigen零零散散的一些总结
  14. 类脑计算的研究进展与发展趋势
  15. 裸金属云FASS高性能弹性块存储解决方案
  16. java并发编程 笔记八
  17. 用 TFserving 部署深度学习模型
  18. 【模拟电路】PN结的相关知识
  19. SAP_销售发票VF01开放可人工修改定价金额
  20. word中在空白处加下划线不显示解决

热门文章

  1. 小孔成像实验探究的软件_小孔成像法观察日食
  2. android 支付宝月账单 统计图_@三明人 支付宝年度账单来了!今天的你晒账单了吗?...
  3. 刷新table数据_关于数据透视表的刷新功能最值得了解的几个操作
  4. django language_「基于Python技术的智慧中医商业项目」Django问诊系统篇-1.系统配置...
  5. java 查看虚拟机状态_深入理解java虚拟机学习笔记(四)虚拟机性能监控与故障处理工具...
  6. linux卡片电脑源码,x4412开发板ibox卡片电脑项目实战9-搭建最简单的linux文件系统...
  7. html 手机复制到剪贴板,移动端和pc端的复制到剪贴板功能
  8. java嵌套类中的方法怎么调用_java类与嵌套嵌套后,怎么使用最外层的类建立对象后使用内部类的方法?...
  9. JAVA写的文件分割与文件合并程序
  10. Visual Studio Code打开项目