dubbo 的可扩展机制 spi源码解析

图1

图1调用的整个过程。

图2

 

图2 url 的定义。

图3

图4

图3 图4表示invoker 

图5

 

图6

图 6spi 定义。




图7 jdk spi 实例 

图7定义了一个接口CarInterface ,图8实现这个接口为BlackCar ,图9 实现了这个接口为redCar,. 图10 21行加载这个接口的实现类从哪里加载,以及加载哪些,按什么顺序加载。图 11做了定义。图12为执行结果。

图8

图9

图10

图11

图12

 


图13

图13 serviceLoader 调用load方法 获取当前线程的 ClassLoader,  调用重载方法 图14,。new ServiceLoad类返回 图15。

图15 判断类加载是不是为空如果为空 ,使用系统类加载器,如果不为空使用传进来的调用 reload()方法图16, 图16创建lookupIterator对象。图18给 service,loader属性赋值。图18 344行 获取类的全路径图20。 346行,348行加载配置文件,获取内容。357行解析文件内容,是否有下一行,有继续读取下一行, 图21;图22 调用nextService() 方法 ,370创建class对象,380行实例对象,放在一个providersMap中,强转返回实例对象。

图140

图15

图16

 

图17

图18 

 

图19

图20

图21

图22



jdk spi 的作用 简单说就是对框架的一种扩展。框架定义一个接口,框架默认定义了一个现实这个接口的实现类。这个时候假设我们想要再次添加一个实现类,我们只需要在配置文件中添加这个实现类,引入自己的jar就可以了,这样框架在执行时扫描这个配置文件就可以来加载自己实现的类。

但是jdk spi 有一个缺点;如果我要获取一个指定的实现类,也要通过遍历和判断来获取图23。如果有很多实现类的情况下回很耗时。有没有一种机制直接获取其中的一个,有dubbo 。dubbo采用 key,value方式在配置文件中进行配置。如果想获取其中的一个,只需要传进去key 就能获取其中的一个图24;

 


图23

图24

 



 

图25

图26

总结:

dubbo 的spi 实现方式:1.在接口类上标注spi注解图25,2.配置文件修改为key,value类型图24。3.图26方式调用。


 

图27

图28

图29

总结:

  dubbo spi 的aop 实现方式:1在配置文件中添加CarWrapper类图27 。2.CarWrapper类实现接口,并重写 getColor方法图28 。3 执行调用 图29。实现了类似spring 的aop功能。 


图30

图31

图32

总结:

图30, 图31,图32。如果在添加一个CarWrapper2 ,如果CarWrapper2配置在配置文件的CarWrpper的下面,那么他优先于CarWrapper,也就是说越下面的Wrapper ,越先执行。


图33

图34

图35

图36

总结:dubbo 的依赖注入; 实现方式 1. 在配置文件 配置 benz=全类名 图36;2. 在CarInterface接口中给重写方法添加@Adative("car")  注解图35。 3.BenzCar实现CarInterface接口,为carInterface属性,添加注入点,添加set方法,在重写方法中调用 接口的carInterface的重写方法图33; 4.新建map 传入key = @Adaptive("car") 的vule值 =car  , value 为要实现类的 key 。放入url中。调用 图34; 的到的结果为:图33;

分析 :

   图34  26行 通过 传入的参数"benz"  从配置文件中获取BenzCar Class 类,再把Class 类实例化为BenzCar 实例对象,调用BenzCar的getColor(url)方法。图33 25行打印 “干啥呢”   .26行执行接口属性的getColor(url) 方法。此时 这个接口 的实现类应该是那个呢?  从url中获取的 map中 key="car" ,value="red" 。根据@Adaptive("car") value car 找到 map中的Key =car 。那么 map中的vlue 为配置文件中对应的key =red  ,然后从配置文件中读取red对应的类 ,转换为Class对象,实例化,调用方法完成。



图35

 

图36

 

图37

 

总结:

图35 21行 ExtensionLoader.getExtensionLoader(CarInterface.class); 原理说明:图36  109行到108行type非空判断,是否接口判断。120行先从map中获取ExtensionLoader, 如果获取不到新建一个放在map中,并且返回.ExtendLoader.对象。 这个map key = type 也就是全类名,value= ExtensionLoader. 也就是说一个接口(type),对应一个一个ExtensionLoader。



图38

图39

图40

总结:

图38 26行获取 Extension类。如果name = true; 采用默认的 的实现类。例如图40 在spi(value="car")   接口类。car 为默认实现类。 图39 331行。图39 334行到349行 ,先从cachedInstances 一个ConcurrentMap <String,Holder<Object>>  中根据name获取一个Holder类。如果Holder 为空,则 以name为key ,new 一个Holder 放在这个cachedInstances 中。从canchedInstances中从新获取Holder对象。从Holder对象中获取 Object对象, Object 对象如果为空 ,则createExtension(name);也就是扩展点,实现类  返回。

图41(0)

图41

总结:

createExtension(name) 的整体流程: 1首先执行图41的519行代码 getExtensionClasses() 方法返回map,从map中获取key为name的Class类型 ,从EXENSION_INSTTANCES map中获取 实例对象。如果为空, 对Class对象 进行实例化,放在map中然后再从map中取出对象。 529行依赖注入和 Aop 执行包装类。

  531行。 判断cachedWrapperClasses 如果不为空,  遍历。 533行 通过 反射调用 wrapperClasses  调用构造器 此构造器含有CarInterface参数的,参数为 BenzCar实例。进行包装。如果有多个进行多个包装最终返回 intstance为 Wrapper类图41(0)。

 

图42

总结:图42  先从缓存中获取classes 如果为空 ,加载 ExtensionClasses 595行。加载后放到map中。从map中取出返回。

a图43

图45

图46

总结:

图 44 605行到 618行,判断CarIntterface 接口是否有spi 注解,从注解中取出值必须是一个,如果是多个报错。取出的值放在cachedDefaultName 中。 620行到626行依次 从 META-INF/dubbo/intternal,   META-INF/dubbo/ ,   ,META-INF/services 中加载文件封装到extensionClasses中。type .getName() 为接口全类名。

图46 通过目录+接口的全类名 找到对应的文件,每个文件生成一个对应一个url, 通过classload 加载多个个文件,遍历并解析每个文件下的内容封装到extensionClasses中取643行。

图47  解析每个文件 。658行到662行循环读取每一行,如果有#号的是注释跳过。663行到672行 以=号分割 ,=号前面是名字,后面是接口实现类全类名图47

图47

图48

 

图49

图50

图51

 

总结: 图48  690-694行 判断传进来的clazz是否实现了CarInterface接口,怎么判断呢,判断clazz的构造器参数是否有CarInterfaces参数,有就是,没有就不是  。注意和是否有Wrapper关键字无关图49,图50。 没有实现抛出异常。 695行到702行,判断clazz是否有adaptive注解。如果有 new ConcurrentHashSet类型的 cachedWrapperClasses ,把class 放在 cachedWrapperClasses中。 

711行-717行 校验name为空的情况,718 行-729行 把name值进行分割。判断clazz是否包含Activate注解,如果包含 就放在cachedActives的map 。 然后遍历names  往cachedNames放入 key =clazz, value=name。

735行  extensionClasses 放在这个map中 key=name, value =clazz .  图51 。这种情况 red,bigreg= com.luban.dubbo_spi.imp.RedCar. 会往 map中放入两天记录。 一种是red=com.luban.dubbo_spi.imp.RedCar.. 另一种是bigreg= com.luban.dubbo_spi.imp.RedCar.  



图52

图52依赖注入注入其他的接口 interface; 注入的接口如果有多个实现类那么应该注册那个实现类呢, 为了解决这个问题引入了子适应实现类。

图53

图53 546行遍历传入实例对象的所有方法,判断方法必须是set开头,且参数类型只有一个,且是public 。556行获取 参数的类型,也就参数的全类名,即CarInterface的全类名,  561行 获取属性,set方法去掉set 前缀 set后的第一字符小写,够成属性名。 例如 setBolck(CarInterface carInterface ) 得到的 property = bolck  .562行 调用 objectFactory==AdaptiveExtensionFactory . 的getExtension()方法,返回动态生成的代理类图67 , 564反射调用这个set方法为 CarInterfase 接口赋值,也就是为接口注入具体的实现类(图53-0)。

图53-0

图53-1 

图53-1 ExtensionFactory的 实现类为 上AdaptiveExtensionFactory,SpiExtensionFactory,SpringExtensionFactroy ,MyExtensionFactry(为兼容)。 图53-2 38行 获取加载 所有ExtensionFactory的实现类,图53-4 373行getExtensionClasses()方法,如果有adaptive注解的 就为默认的adaptive实现类;图53-2 初始化AdaptiveExtensionFactory时,把图531所由的默认实现类放在一个List集合中factories中。图53-3 依次调用各个实现类的getExtension() 方法。如果不为空就返回不再继续遍历。当调用SpiExtensionFactory时,图53-5 type 为接口,且 注解含有SPI满足条件执行load.getAdaptiveExtension();(同图59 100行)方法获取代理对象图67。

图53-2

图53-3

 图53-4

图53-5

图53-6

总结:

图53开始依赖注入,545行 objectFactory从哪里来赋值的呢.  图54  21行调用getExtensionLoader(CarInerface.class) 获取ExtensionLoader。 图55 122行 new ExtensionLoader(type) 为objectFactory赋值。  图58 ,图59 判断是否type ==ExtensionFactory , 第一次type为CarInterface. 执行 图59 getExtensionLoder(ExtensionFactory.class)  。该 调用.adaptiveExtension() 方法获取自适应类objectFactory =AdaptiveExtensionFactory(图53-1)。 

图54

图55

图58

图59

图60

图61

图61 先从cachedAdaptiveInstance 缓存中获取,如果获取不到调用 createAdaptiveExtension()

图62

3

图63

图64

 

.图64  770行 调用getAdaptiveExtensionClass() 方法 获取对应的Clazz对象,创建实例 。  777行getExtensionClasses() 方法首先从,所有的接口文件从 META-INF/dubbo/intternal,   META-INF/dubbo/ ,   ,META-INF/services 的获取所有的 该接口的实现类判断是否有类上有adaptive注解,有注解会放置在cachAdaptiveClass 属性中返回。781行 如果没有则生成一个代理类。 图65 785行为生成的代理类字符串,然后进行加载和编译。为class 对象返回。 图66位详细生成过程。 图67位生成的代理类。CarInterface 的代理类为 CarInteface$Adaptive 类.这个类从url获取对应的实现类 从配置文件中加载。 图68 ,图69 adaptive注解可以注解在类上也可以注解在方法上。

图65

图66

图67

图68

 

图69

 




 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值