当前位置:   article > 正文

[Dubbo源码剖析]ExtensionLoader机制

extensionloader

纵观Dubbo源码,无处不涉及到底层的SPI扩展载入机制,故本节以SPI扩展载入机制作为Dubbo源码剖析的入口
本节涉及内容
1.ExtensionLoader<T>类核心方法剖析
2.举例说明Dubbo的SPI机制,并演示如何实现自定义扩展




--------------------1.ExtensionLoader<T>类核心方法剖析-----开始-------------------
A。关于ExtensionLoader<T>类的核心字段说明(大写字段属性,代表全局共享类变量
SERVICES_DIRECTORY、DUBBO_DIRECTORY、DUBBO_INTERNAL_DIRECTORY:定义dubbo底层读取SPI配置的目录
EXTENSION_LOADERS:存放已经实例化的ExtensionLoader<T>,以Class<T>为key
EXTENSION_INSTANCES:存放已经实例化的对象(对象实现了T接口),以Class<? super T>为key
私有字段
type:SPI接口的字节类
objectFactory:AdaptiveExtensionFactory实例对象,通过ExtensionLoader<ExtensionFactory>载入器获取


注:ExtensionLoader<T>,就像一个维护实现T接口的所有子类的字节类以及子类的实例类集合的一个工厂。


B。ExtensionLoader<T>类定义了实现SPI机制的各种方式
ExtensionLoader<T>的方法大概念分为两大类:基本机制实现方法与自适应扩展方法,自适应扩展方法第C节讲
基本机制实现方法:其中getActivateExtension(URL url, String[] values, String group)方法几乎涉及到所有方法的调用,
以下列出以访方法为入口的方法调用栈
List<T> getActivateExtension(URL url, String[] values, String group)//根据扩展名(values)数组,组名,Url对象,获取所有激活的实例
》》》1.getExtensionClasses()//初始化当前ExtensionLoader<T>类的扩展实现类字节集合(即所有实现了T接口的字节类集合)cachedClasses
》》》2.根据names扩展名数据,调用getExtension(name),获取实例集合并返回
public T getExtension(String name)//根据name,获取实例
》》》1.name为“true”时,获取默认实例getDefaultExtension(),
》》》2.如果不存在则调用createExtension(String name)
T  createExtension(String name)//根据名称创建扩展实例
》》》1.从缓存字节类集合cachedClasses,查找Class<T>clz
》》》2.根据Class<T>clz,从全局实例变量EXTENSION_INSTANCES,获取实例
》》》3.如果EXTENSION_INSTANCES不存在,则Class<T>.newInstance()创建实例,并加入EXTENSION_INSTANCES

》》》4.对实例的所有set方法,进行字段填充(填充实例从objectFactory工厂中获取)

》》》5.最后一步,很重要Protocol里有体现,对实现类进行多层包装实例化,并返回

Map<String, Class<?>> getExtensionClasses()//获取缓存扩展字节类cachedClasses,
》》》1.判断cachedClasses是否存在,存在返回
》》》2.如果不存在,调用loadExtensionClasses()加载
Map<String, Class<?>> loadExtensionClasses()//加载扩展实现字节类集合cachedClasses
》》》1.如果接口T上注解SPI,并注解值存在(如@SPI("name"))设置ExtensionLoader<T>的默认名称(cacheDefaultName变量='name')
》》》2.loadFile()加载配置,并通过反射获取字节类
void loadFile(Map<String, Class<?>> extensionClasses, String dir) //读取指定目录下的文本配置,通过反射获取字段类,例子见下面
》》》1.读取METE-INF/dubbo/internal/、METE-INF/dubbo/、METE-INF/dubbo/services类型路径下的配置文件
》》》文件名:
》》》cn.net.zhaozhiwen.spi.Nameable
》》》内容  :
》》》a=cn.net.zhaozhwen.spi.support.ANameable

》》》b=cn.net.zhaozhwen.spi.support.BNameable

》》》2.通过反射Class.forName("cn.net.zhaozhwen.spi.support.ANameable",true,classloader)获取字节类,

》》》3.以name为key,以字节类为值,存放于ExtensionLoader<T>的cachedClasses里


C。自适应扩展方法
Dubbo提供两种形式的自适应方式
C-1.静态自适应
比如接口A注解了SPI,如下
package cn.net.zhaozhiwen.spi;
@SPI
public interface A{}
有两个类实现了A接口,比如B,C
public class B implements A{}
public class C implements A{}
那么可以在B或C类当中的一个类上面注解@Adaptive注解,(有且只能注解众多实现中的一个类)
假设B上注解了Adaptive,通过ExtesionLoader.getExtesionLoader().getAdaptiveExtension()时就返回B实例
(调用getExstensionClasses()时,根据注解的情况设置变量cachedAdaptiveInstance)


C-2.动态自适应(适应于只定义SPI接口,没有实现类的情况)
在调用getAdaptiveExtension()时,如果cachedAdaptiveInstance为null时,就会去读接口方法上的Adaptive注解,
通过反射与动态编译来获取Class字节类,例如以下例子
package cn.net.zhaozhiwen.spi;
@SPI
public interface D{
   @Adaptive
   void hello(String name);
}




C-3:自适应扩展方法的源码分析
以下面的方法作为入口
public T getAdaptiveExtension()
》》》1.cachedAdaptiveInstance不为空,返回
》》》2.否则调用createAdaptiveExtension()方法
private T createAdaptiveExtension()
》》》1.获取适应字节类getAdaptiveExtensionClass()//cachedAdaptiveClass为null,调用createAdaptiveExtensionClass()
》》》2.实例并调用injectExtension(T t)//利用set方法,使用objectFactory的实例注入
private Class<?> createAdaptiveExtensionClass()
》》》1.通过接口的定义与注解,生成源码,createAdaptiveExtensionClassCode(),实现类,T的完整类名+$Adaptive

》》》(如:C-1-2中的D接口,动态生成 名为 D$Adaptive 的实现类。源码的内容如下:

》》》》a. 如果接口方法上没有注解@Adaptive,则抛出异常

》》》》b. 如果接口方法有参数URL对象,但是该对象为null,则抛出异常

》》》》c. 获取@Adaptive的value值,未找到就使用T简单类名的驼峰转点分类路径来作用为value

》》》》d.根据上面的value,通过url.getMethodParmeter(value)或url.getParmeter(value)(或者value为protocol,则调用url.getProtocol()),来获取扩展名称extName

》》》》e.最后调用ExtensionLoader.getExtensionLoader(T.class).getExtension(extName),获取实例,并调用与接口声明的同名方法

》》》)

》》》2.通过Compiler实现类JavassistCompiler,将源码编译成Class<T>
注:核心入口方法有2个
getActivateExtension:作为整个机制的入口方法
getAdaptiveExtension:实现自适应扩展


--------------------1.ExtensionLoader<T>类核心方法剖析-----结束-------------------




--------------------2.举例说明Dubbo的SPI机制,并演示如何实现自定义扩展---------开始---------------
此部分作为练习环节,有兴趣的同志们可以玩一玩
1.创建普通JavaProject工程,并加入dubbo框架的maven依赖


2.编写扩展SPI接口和两个实现类
比如Capable,包名为com.my.dubbo,并在接口上注解@SPI("a")
package com.my.dubbo
@SPI("a")
public interface Capable{
      void hello();
}
package com.my.dubbo.support;
public class ACapable implements Capable{
      void hello(){
       //A
      }
}
@Adaptive
public class BCapable implements Capable{
      void hello(){
       //B
      }

}


3.在/src/main/resource/目录下,创建目录METE-INF/dubbo/和文件com.my.dubbo.Capable,文件内容如下:
a=com.my.dubbo.support.ACapable
b=com.my.dubbo.support.BCapable


4.编写一个main方法测试
ExtensionLoader<Capable> capable = ExtensionLoader.getExtensionLoader(Capable.class);
//判断是否获取到BCapable实例
Capable aCapable = capable.getExtension("a");
//判断是否获取到默认实例,即SPI后面配置的名字,即ACapable实例
//等价于调用capable.getDefaultExtension();
Capable defaultCapable = capable.getExtension("true");
//判断是否同一个实例
System.out.println(defaultCapable==aCapable)
//判断是否获取到BCapable实例
Capable bCapable = capable.AdaptiveExtension();

5.包裹类

public class ABCapableWrapper implements Capable{
      private Capable capable;

      public ABCapableWrapper (Capable capble){

           this.capable = capable;

      }

      void hello(){
          //AB
      }

}

那么通过ExtensionLoader获取的实例T,将被包裹类封装起来,dubbo使用这一特性,为Protocol实例进行了封装,用以实现对Invoker的过滤链拦截与监听器的设置。


--------------------2.举例说明Dubbo的SPI机制,并演示如何实现自定义扩展---------结束---------------

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/658186
推荐阅读
相关标签
  

闽ICP备14008679号