面试常问的dubbo的spi机制到底是什么?(上)

简介: dubbo的spi机制详解

目录

  • 什么是spi机制?
  • java的spi机制 -- ServiceLoader
  • spring的spi机制 -- SpringFactoriesLoader
  • dubbo的spi机制 -- ExtensionLoader 源码剖析
  • 总结

公众号三友的java日记

前言

dubbo是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。作为spring cloud alibaba体系中重要的一部分,随着spring cloud alibaba在国内活跃起来,dubbo也越来越深受各大公司的青睐。本文就来对dubbo的spi机制源码进行剖析,看一看dubbo的spi到底有哪些特性和功能。

什么是spi机制?

SPI (Service Provider Interface),主要用于扩展的作用。举个例子来说,假如有一个框架有一个接口,他有自己默认的实现类,但是在代码运行的过程中,你不想用他的实现类或者想扩展一下他的实现类的功能,但是此时你又不能修改别人的源码,那么此时该怎么办?这时spi机制就有了用武之地。一般框架的作者在设计这种接口的时候不会直接去new这个接口的实现类,而是在Classpath路径底下将这个接口的实现类按作者约定的格式写在一个配置文件上,然后在运行的过程中通过java提供的api,从所有jar包中读取所有的这个指定文件中的内容,获取到实现类,用这个实现类,这样,如果你想自己替换原有的框架的实现,你就可以按照作者规定的方式配置实现,这样就能使用你自己写的实现类了。

spi机制其实体现了设计思想中的解耦思想,方便开发者对框架功能进行扩展。

java的spi机制 -- ServiceLoader

java中最常见的spi机制应用就是数据库驱动的加载,java其实就是定义了java语言跟数据库交互的接口,但是具体的实现得交给各大数据库厂商来实现,那么java怎么知道你的数据库厂商的实现了?这时就需要spi机制了,java好约了定在 Classpath 路径下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后内容是该数据库厂商的实现的接口的全限定名,这样数据库厂商只要按照这个规则去配置,java就能找到。

我以mysql来举例,看一下mysql是怎么实现的。

image.png

内容

image.png

java是通过ServiceLoader类来实现读取配置文件中的实现类的。大家有兴趣可以看一下里面的代码,其实就是读取到每个jar包底下的文件,读取里面的内容。

spring中的spi机制 -- SpringFactoriesLoader

相信spring大家都不陌生,在spring扩展也是依赖spi机制完成的,只不过spring对于扩展文件约定在Classpath 路径下的 META-INF目录底下,所有的文件名都是叫spring.factories,文件里的内容是一个以一个个键值对的方式存储的,键为类的全限定名,值也为类的全限定名,如果有多个值,可以用逗号分割,有一点得注意的是,键和值本身约定并没有类与类之间的依赖关系(当然也可以有,得看使用场景的约定),也就是说键值可以没有任何关联,键仅仅是一种标识,代表一种场景,最常见的自动装配的注解,@EnableAutoConfiguration,也就是代表自动装配的场景,当你需要你的类被自动装配,就可以以这个注解的权限定名键,你的类为名,这样springboot在进行自动装配的时候,就会拿这个键,找到你写的实现类来完成自动装配。

这里我贴出了自动装配时加载类的源码。

image.png

这里其实就是通过@EnableAutoConfiguration的全限定名从spring.factories中加载这个键对应的所有的实现类的名称,这样就能拿到所有需要自动装配的类的全限定名了。

mybatis整合spring的自动装配功能文件

image.png

内容

image.png

mybatis也是按照spring的规则来配置的。大家有空可以去看MybatisAutoConfiguration这个实现类,里面有mybatis是如何跟spring整合的内容。

SpringFactoriesLoader的应用场景还有很多,大家可以去看一下SpringBoot中的启动引导类SpringApplication,里面多次使用到了这个SpringFactoriesLoader这个类来获取各种实现。

dubbo的spi机制 -- ExtensionLoader源码剖析

本文是基于dubbo3.0.4版本源码剖析。

讲完了java和spring的中的spi机制,接下来进入本文的主题,dubbo的spi机制到底是什么?它与java自带的有何区别?为什么不用java的spi机制?

ExtensionLoader是dubbo的spi机制所实现的类,通过这个类来加载接口所有实现类,获取实现类的对象。同时每一个接口都会有一个自己的ExtensionLoader。

1)java的spi机制的缺点?

从我们分析java的spi机制可以看出,java约定了文件名为接口的名称,内容为实现。不知道大家有没有想过这里面有个很严重的问题,就是虽然我获取到了所有的实现类,但是无法对实现类进行分类,也就是说我无法确定到底该用哪个实现类,并且java的spi机制会一次性给所有的实现类创建对象,如果这个对象你根本不会使用,那么此时就会白白浪费资源,也就是说无法做到按需加载。

所以,dubbo就自己实现了一套spi机制,不仅解决了以上的痛点,同时也加入了更多的特性。

2)dubbo的配置文件约束。

dubbo会从四个目录读取文件META-INF/dubbo/internal/ 、META-INF/dubbo/ 、META-INF/services/、META-INF/dubbo/external/,文件名为接口的全限定名,内容为键值对,键为短名称(可以理解为spring中的对象的名称),值为实现类。

3)@SPI 注解的约束

dubbo中所有的扩展接口,都需要在接口上加@SPI注解,不然在创建ExtensionLoader的时候,会报错。代码体现在这里

image.png

顺便说说ExtensionDirector的作用,在3.0.3以前的版本,是没有这个类的,但是在之后的版本为了实现一些新的特性,就抽象出来了这个类,通过这个类来获取每个接口对应的ExtensionLoader

4)实现类的加载

先说各种特性之前,先说一下这些实现类是如何加载的,类的加载是非常重要的一个环节,与后面的spi特性有重要的关系。

类加载默认都是先调用getExtensionClasses这个方法的,当cachedClasses没有的时,才会去加载实现类,然后再把实现类放到cachedClasses中。真正实现加载的是loadExtensionClasses 方法,接下来我们详细看这个方法的源码。

image.png

checkDestroyed();

方法没什么东西,其实就是一个检查的作用。

cacheDefaultExtensionName();

缓存默认实现类的短名称。其实很简单,就是从@SPI注解中取出名称,就是默认的实现类的名称,缓存起来,ExtensionLoader有个getDefaultExtension方法,其实就是通过这个短名称对应的实现类的对象。

接下来会遍历LoadingStrategy,根据LoadingStrategy加载指定目录的文件。

我们先来看看LoadingStrategy的实例是怎么加载的。我们进入loadLoadingStrategies方法,

image.png

惊讶的发现竟然是使用了java的spi机制加载LoadingStrategy,那我们就去Classpath 路径下的 META-INF/services/路径下找这个LoadingStrategy接口的全限定名的文件,看看有哪些实现。有四个实现,也就是会按照这四个的加载策略来读取实现类。其中有个方法directory,就是指定加载的目录,这也就是我们前面说的那几个dubbo会加载的目录,其实是从这个方法返回的,你可以自己去看看这四个实现类对于这个方法的实现。其实我们也可以实现这个接口,指定我们自己想加载的目录。

这里会循环加载每个目录,我们进去loadDirectory方法。

image.png

这个其实就是拿出LoadingStrategy来调用重载的loadDirectory方法。

这里注意会调用两次loadDirectory,下面的那个其实是适配以前老版本的,不用关心。

接下来进去重载的loadDirectory方法。

image.png

可以看出,fileName就是LoadingStrategy所指定的目录 + 接口的全限定名,这里就解释了为什么实现类需要写在类全限定名的文件里。其实就是从每个jar底指定的目录类全限定名为名称的文件,得到每个jar底下的文件的URL。然后遍历每个URL,加载类,我们进入loadResource方法来看看具体是怎么加载的。

image.png

通过URL打开一个输入流,然后读取文件内容,取出每一行,以 = 进行分割(因为规定的是以键值对存的),键就是短名称,值就是实现类的名称,然后再进入loadClass方法,这个方法很重要,其实是对实现类进行一个分类,后面dubbo的特性实现的前提就是对这些实现类的分类操作。

image.png

image.png

标红的两处是这个意思

如果你加了@Adaptive注解,那么就将赋值到cachedAdaptiveClass属性上。我们叫这个类为自适应类。什么是自适应,其实说白了这个类本身并没有实际的意义,它是根据你的入参动态来实现找到真正的实现类来完成调用。getAdaptiveExtension其实就是获取到这个自适应实现类对应的对象。

如果你的实现类是有一个该类型为参数的构造方法,那么就将这个实现类放到cachedWrapperClasses中,并且我们称这个类为包装类,什么叫包装,其实跟静态代理有点像,就是将目标对象进行代理,可以增强功能。

image.png

这处标红的意思是判断是不是实现类是不是加了@Activate注解,是的话就将短名称和注解放入cachedActivates中,我们称这类实现类为自动激活的类,所谓的自动激活,就是可以根据你的入参,动态选择实现一批符合条件的实现类

image.png

saveInExtensionClass就是将这个实现类放入extensionClasses中,该目录下的实现类就加载完成了。

接下来会继续循环,加载不同的目录底下,都会进行分类,并放到extensionClasses中。

当LoadingStrategy循环玩之后,最后将extensionClasses放入cachedClasses中,此时就完成了对于指定目录下实现类的加载和分类。

至此,实现类的加载和分类就完成了。

由于篇幅有限,dubbo的spi机制的其它各种特性和功能,比如依赖注入、自适应、自动包装等功能,我会再写一篇文章跟大家分享。

总结

本文最开始先介绍了什么是spi机制,然后分析了java的spi机制和spring的spi机制,最后我们进入本文的主题,dubbo的spi机制,我们讲了实现类的加载,加载实现类的时候会对实现类进行分类。由于篇幅有限,dubbo的spi机制的其它的特性功能我将在下篇文章继续分享,欢迎大家持续关注本公众号,谢谢大家。

PS:如果觉得这篇文章对你有帮助,欢迎大家关注公众号三友的java日记、分享、点赞、在看。

相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
17天前
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
22天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
26天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
46 1
|
28天前
|
监控 架构师 Java
从蚂蚁金服面试题窥探STW机制
在Java虚拟机(JVM)中,垃圾回收(GC)是一个至关重要的机制,它负责自动管理内存的分配和释放。然而,垃圾回收过程并非没有代价,其中最为显著的一个影响就是STW(Stop-The-World)机制。STW机制是指在垃圾回收过程中,JVM会暂停所有应用线程的执行,以确保垃圾回收器能够正确地遍历和回收对象。这一机制虽然保证了垃圾回收的安全性和准确性,但也可能对应用程序的性能产生显著影响。
25 2
|
30天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
2月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
66 8
|
3月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关,并且消息处理机制在整个Android知识体系中也是尤其重要,在太多的源码分析的文章讲得比较繁琐,很多人对整个消息处理机制依然是懵懵懂懂,这篇文章通过一些问答的模式结合Android主线程(UI线程)的工作原理来讲解,源码注释很全,还有结合流程图,如果你对Android 消息处理机制还不是很理解,我相信只要你静下心来耐心的看,肯定会有不少的收获的。
198 3
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
3月前
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
3月前
|
Java
【Java基础面试四十七】、 说一说你对Java反射机制的理解
这篇文章介绍了Java反射机制,它允许程序在运行时获取对象和类的真实信息,进行类和实例的创建,以及访问和修改成员变量和方法。