Dubbo——扩展点加载机制

扩展点加载机制


        只有标有@SPI注解的接口类才会查找扩展点的实现,依次从下面这三个路径读取扩展点文件:META-INF/dubbo/internal 、META-INF/dubbo/ 、META-INF/services/,其中dubbo内部实现的各种扩展文件都放在META-INF/dubbo/internal目录下面。

 

以com.alibaba.dubbo.rpc.ProxyFactory文件为例,该文件中的内容如下

1 获取ExtensionLoader实例

ExtensionLoader.getExtensionLoader(Class<T>type)方法:调用此静态方法获得ExtensionLoader对象实例,该方法对于每个Class对象创建一个ExtensionLoader对象实例。大致逻辑如下:

1)检查type接口类是否使用了@SPI注解,只有使用了该注解的接口类才会查找扩展点实现类;否则抛异常;

2)从全局静态变量ExtensionLoader.EXTENSION_LOADERS:ConcurrentHashMap<Class<?>,ExtensionLoader<?>>中查找是否已经存在扩展点type的ExtensionLoader对象实例,若没有则调用私有构造函数ExtensionLoader(Class<?>type);

3)在该私有构造函数中,完成ExtensionLoader对象的两个成员变量的赋值,第一,成员变量type:Class赋值为入参(扩展点type);第二,成员变量objectFactory:ExtensionFactory,若type为ExtensionFactory.class,则objectFactory=null,否则通过调用ExtensionLoader. getExtensionLoader方法获取ExtensionFactory.class的扩展点的ExtensionLoader实例,然后调用getAdaptiveExtension方法获取扩展点实现类的适配器类实例。代码如下:

2 getAdaptiveExtension()方法

该方法主要是获取扩展点实现类的适配器类实例。首先从缓存变量ExtensionLoader.cachedAdaptiveInstance中获取,若为空,则调用createAdaptiveExtension方法创建适配器类的实例,并将适配器类的实例存入缓存变量ExtensionLoader.cachedAdaptiveInstance中。内部的调用逻辑如下:


 

2.1 createAdaptiveExtension方法

该方法完成适配器类的实例化并对适配器类的实例对象中类型为扩展点接口类型的成员变量初始化值。

1)调用getAdaptiveExtensionClass方法,创建适配器类的class对象;

2)调用Class.newInstance方法完成class对象的实例化;

3)调用injectExtension方法完成实例化对象中成员变量的初始化工作。

 2.1.1 getAdaptiveExtensionClass方法

获取扩展点实现类的Map对象,该Map对象的key为每个类简称,value为class对象。

1)检查缓存ExtensionLoader.cachedClasses 变量中是否有值,若有则直接返回;

2)若没有缓存,则调用loadExtensionClasses方法获取Map对象,并存入ExtensionLoader.cachedClasses变量中。

2.1.1.1 getExtensionClasses方法

获取扩展点实现类的Map对象,该Map对象的key为每个类简称,value为class对象。

1)检查缓存ExtensionLoader.cachedClasses 变量中是否有值,若有则直接返回;

2)若没有缓存,则调用loadExtensionClasses方法获取Map对象,并存入ExtensionLoader.cachedClasses变量中。

 loadExtensionClasses方法主要是加载扩展点的实现类,业务逻辑如下:

1)获取ExtensionLoader.type参数值(getExtensionLoader函数的入参)的类的SPI注解,获取该注解的value值,若该值不为空且只有一个值的话,则保存ExtensionLoader.cachedDefaultName对象,作为默认实现类使用;若为空则表示没有指定默认的实现类;但SPI注解的value值一定不能设置多个。

2)调用loadFile方法解析以ExtensionLoader.type的类名来命名的文件内容,并返回Map对象,该对象的每个K-V即为文件中的一行内容,key=类简称,value=class对象;

loadFile方法:逐行读取以ExtensionLoader.type参数值的类名来命名的文件内容;ExtensionLoader.type的值是在ExtensionLoader.getExtensionLoader方法调用时传入的。以ExtensionFactory为例,文件内容的格式如下。

对每行的解析工作如下:

1)等号右边为实现类的类名。等号左边为该类的简称;若只写类名,则以一定的规则来获取类的简称,具体见下面的步骤实现;左边的简称也可以是以逗号分隔的多个简称;

2)对等号右边的类使用Class.forName方法创建class对象,后续对该class对象进行操作;

3)该class对象必须实现了ExtensionLoader.type参数指定的接口;

4)该class对象是否使用了@Adaptive注解,若使用了则将该class对象保存到ExtensionLoader.cachedAdaptiveClass 变量中,在每个文件中的所有实现类最多只能有一个类使用了@Adaptive注解;该行解析结束,继续下一行;若没有使用@Adaptive注解的类,则进行下面的解析步骤;

5)该class对象是否有ExtensionLoader.type参数的构造函数,若有则表示可以进行封装,将该class存入ExtensionLoader.cachedWrapperClasses 变量中,该行解析结束,继续下一行的解析;例如Protocol接口的扩展点文件中的ProtocolFilterWrapper类的记录,该实现类有一个Protocol参数的构造函数,故是在创建Protocol的扩展点实现类的实例时用该类封装一层,若没有,则继续下面的解析步骤;

6)若等号左边为空或者该行没有等号,则检查该class对象是否使用了@Extension注解,若使用了则取该注解的value值作为该class对象的简称;若没有使用@Extension注解,但实现类的类名是以接口名结尾的,则取类名的前部分为简称;但若不是以接口名结尾的类名,则以类名为该类的简称;

7)检查class对象是否使用了@Activate注解;若使用则获取该注解,并存入ExtensionLoader.cachedActivates对象中,key为类的简称,若有多个简称则取第一个;

8)遍历所有的类简称,将class对象和每个类简称存入ExtensionLoader.cachedNames对象中,其中key=class对象,value=类简称;将每个类简称和class对象存入Map对象并返回,key=类简称,value=class对象;

 

2.1.1.2 createAdaptiveExtensionClass方法

该方法在运行时动态创建适配器类。

1)动态生成适配器类的代码,下面列出了部分接口的动态适配器类的代码:CacheFactory、Cluster、ConfiguratorFactory、Dispatcher、HttpBinder、MonitorFactory、Protocol、ProxyFactory、RegistryFactory、RouterFactory、ThreadPool、Transporter、Validation、ZookeeperTransporter,具体代码见《附件一》。在生成适配器类时,若接口中没有声明带@Adaptive注解的方法,则不生成适配器类,若有,则在生成适配器类时,对带注解的方法重写;

2)编译适配器类代码,创建Class对象。编译器也是通过ExtensionLoader类来动态扩展的,默认选择JavassistCompiler编译器。

2.1.1  injectExtension方法

对于非ExtensionFactory.Class对象,利用ExtensionFactory的扩展实现类的对象获取成员变量的初始化值,实现代码如下:

1)获取公有的set方法,解析出成员变量的名称。

2)调用ExtensionFactory.getExtension方法获取成员变量的值。

3)通过反射的方式调用set方法完成成员变量值的注入。


上述代码中的ExtensionFactory.getExtension方法:在ExtensionFactory的文件中,包含了AdaptiveExtensionFactory类,该类使用了@Adaptive注解,所以ExtensionFactory接口的适配器为AdaptiveExtensionFactory类,故调用的是该适配器类的getExtension方法。

1)在该方法中遍历ExtensionFactory扩展文件中的所有ExtensionFactory对象的getExtension方法,代码如下:


2)目前有SpiExtensionFactory和SpringExtensionFactory两种;第一,对于SpiExtensionFactory.getExtension方法,根据成员变量的类型从扩展点中加载该对象,代码如下:


第二,对于SpringExtensionFactory.getExtension 方法,从Spring的上下文中获取该成员变量的Bean对象,代码如下:


2.1 getExtension(String name)方法

ExtensionLoader.getExtension(Stringname)方法:返回指定名字的扩展类实例,调用顺序图如下:

大致逻辑如下:

1)如果指定名字的扩展类不存在,则抛异常。

2)如果指定的名字为“true”,则返回默认的实现类实例,即name= cachedDefaultName;

3)从ExtensionLoader.cachedInstances:ConcurrentHashMap变量中获取该name的实例;

4)若Map中没有该name的实例,则调用createExtension方法创建该实例,并保存到缓存中。

 1、createExtension(String name)方法:创建扩展点实现类的实例对象。

1)调用getExtensionClasses方法,获取type参数的文件内容;并从Map对象中获取name的Class对象,若没有则抛异常;

2)从全局变量ExtensionLoader.EXTENSION_INSTANCES:ConcurrentMap<Class<?>, Object>中获取该class的实例,若没有,则创建该Class对象的实例,并缓存到上面的Map变量中;

3)调用injectExtension方法完成成员变量的初始化值注入工作;

4)遍历cachedWrapperClasses变量,利用以type接口为参数的构造函数创建每个封装类,故对扩展类的实例进行了封装。并调用injectExtension方法对封装类的成员变量进行初始化工作;

5)返回的实例是经过封装之后的实例对象。

 

2.2  getActivateExtension(URL url, String key, String group)方法

ExtensionLoader.getActivateExtension(URLurl, String key, String group)方法:主要是获取当前扩展的所有可自动激活的实现类的实例列表exts:List<T>。调用顺序图如下:


大致逻辑如下:

1)调用getExtensionClasses方法,加载当前扩展接口的所有实现,会获取到当前Extension中所有标有@Active注解的实现类,存入Map变量cachedActivates中,其中key值为类名;

2)遍历cachedActivates集合,检查集合中每个类中@Active注解的group值是否与入参的值一致,若一致再检查@Active注解的value值是否在URL参数中,若存在则以cachedActivates变量的key值为参数调用ExtensionLoader.getExtension(Stringname)方法获取@Active注解类的实例并存入exts列表中;

3)使用ActivateComparator对exts列表的集合进行排序,排序的依据主要是@Active注解的before、after、order的值来定;

4)以入参key为参数调用ExtensionLoader.getExtension(Stringname)方法,获取key值指定名字的扩展类实例,并将该实例存入exts列表中。例如在服务暴露过程中对Invoker代理对象进行过滤器链的封装时,key=“service.filter”,则从URL中获取该参数的值,该值是在<dubbo:service>标签的filter属性中配置的服务调用过程拦截器名称,多个名称用逗号分隔;在服务引用过程中对Invoker代理对象进行监听器链的封装时,key=“invoker.listener ”;

5)最后将所有的exts列表返回。




没有更多推荐了,返回首页