欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Dubbo源码通~ExtensionLoader

程序员文章站 2022-07-09 13:46:45
...

ExtensionLoader

Dubbo SPI官方文档

1、 主要功能

  • 获取ExtensionLoader:getExtensionLoader(Class<?> type):每个扩展应一个ExtensionLoader
  • 扩展类集合:getExtensionName(Class<?> type)
  • 创建扩展类对象:getExtension(String name)
    • 扩展点自动包装:Wrapper类
    • 扩展点自动装配:注入依赖的属性
    • 扩展点自适应:getAdaptiveExtension()
    • 扩展点自动**
  • 扩展点自动**:getActivateExtension(URL url, String[] values, String group)

2、主要角色

  • ExtensionLoader:扩展点加载器
  • ExtensionFactory
    • AdaptiveExtensionFactory:ExtensionFactory自适应扩展实现类
    • SpiExtensionFactory:扩展点(@SPI)自适应扩展实现类工厂
  • @Adaptive:自适应扩展
  • @Activate:自动**
  • @SPI:扩展点

3、核心方法概述

3.1、创建ExtensionLoader

  • 方法: getExtensionLoader(Class type)获得扩展接口的对应的ExtensionLoader。
    • 从EXTENSION_LOADERS缓存中获取,没有则新建;
    • 创建新的ExtensionLoader(先赋值两个属性:type和objectFactory):
      • 若type为ExtensionFactory,则objectFactory为null
      • 其他类型,则递归调用ExtensionLoader.getExtensionLoader(ExtensionFactory.class) 创建一个ExtensionFactory对应的ExtensionLoader;
      • 再通过ExtensionFactory的ExtensionLoader去加载对应的自适应扩展类AdaptiveExtensionFactory和SpiExtensionFactory。
    • 最后为type创建的ExtensionLoader中的objectFactory对应的就是ExtensionFactory的自适应实现类。
    • 每个type持有一个objectFactory的是为了后续实现属性注入injectExtension

3.2、指定类获取扩展实例

  • getExtensionName(Class<?> type)指定拓展类的名称,若cachedNames缓存中没有,则获取加载配置文件中指定的所有需要拓展类,并缓存到cachedClasses。

    • getExtensionClasses:获得拓展实现类数组
    • loadExtensionClasses:从多个配置文件,拓展实现类数组(解析@SPI 注解,指定cachedDefaultName;加载配置文件里的实现类全限定名)
    • loadDirectory:从配置文件中,加载拓展实现类数组
    • loadResource:遍历文件里的所有拓展实现类,以“=”隔开的配置,key-value存储到extensionClasses中。
    • loadClass:根据拓展类的注解,分开缓存各种拓展类(cachedAdaptiveClass、cachedWrapperClasses、cachedActivates、cachedNames)
  • getExtension(String name):获取指定扩展名的实现类

    • createExtension(name)
    • getExtensionClasses().get(name)
    • 实例化扩展类:instance
    • 扩展点注入:injectExtension(instance)
    • 若是包装类,则instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))
  • getAdaptiveExtension() : 扩展自适应实现类

    • createAdaptiveExtension()
      • 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
      • 通过反射进行实例化
      • 调用 injectExtension 方法向拓展实例中注入依赖
    • getAdaptiveExtensionClass()
      • 调用 getExtensionClasses 获取所有的拓展类
      • 检查缓存,若缓存不为空,则返回缓存: cachedAdaptiveClass
      • 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类。(若没有自适应扩展类,则自动创建)
        • createAdaptiveExtensionClassCode(): 动态生成对应扩展接口的所有方法的实现。
    • injectExtension()
  • getActivateExtension(URL):根据URL创建自**扩展扩展类。


  • ExtensionLoader初始化示意图
    Dubbo源码通~ExtensionLoader

  • ExtensionLoader涉及方法关联图
    Dubbo源码通~ExtensionLoader

4、@Adaptive自适应扩展

  • 触发加载自适应扩展的入口:
    • getAdaptiveExtension()
    • getActivateExtension()
    • getDefaultExtension()
    • getExtension()

每个方法内部都会主动去调用getExtensionClasses()方法,其内部就是加载指定路径下的配置,以此来完成各种扩展类的加载。

  • 调用方式:ExtensionLoader.getExtensionLoader(Class type).getAdaptiveExtension()

    • 若已经加载过对应接口的扩展实现且缓存cachedAdaptiveInstance中有对应的扩展实现类,则直接返回
    • 若没有则再加载一遍,还是没有(一般在服务启动之后就确定加载完了),则会自动动态生成一个自适应实现类。
  • 生成自适应实现类需要的条件

    • 需要装配的扩展实现类有被@Adaptive注解修饰。
    • 扩展接口内,必须至少有一个方法被 @Adaptive 注解修饰,否则,所有方法没有都被@Adaptive 注解修饰的方法,则会直接在方法体内丢异常throw new UnsupportedOperationException

4.1、@Adaptive使用范围

Adaptive 可注解在类或方法上。

  • 标注在类上:Dubbo 不会为该类生成代理类。
  • 标注在方法上:Dubbo 则会为该方法生成代理逻辑,表示当前方法需要根据 参数URL 调用对应的扩展点实现。

4.2、调用方式概述

  • 解决“扩展点自动装配”中如何确定装配的实现类问题?
    • ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用的扩展点实现。
  • 通过调用方的URL参数来确定使用哪一个扩展点实现类
    • 从URL从获取扩展点的名称(接口名称)
    • 通过SPI 加载具体的扩展点实现类
    • 调用目标方法

4.3、举个栗子:Protocol

  • Protocol接口
//☆☆---Protocol
@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();
    
     //1. 服务暴露主功能入口
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

     //2. 服务引用主功能入口
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    
    void destroy();
}
  • dubbo本身没有为Protocol创建自适应扩展类,但在方法调用时会通过ExtensionLoader动态生成
package org.apache.dubbo.rpc;

import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;

@Adaptive
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {

    //NOTE: destory()和getDefaultPort() 都没有@Adaptive注解,所以不会创建具体实现
    public void destroy() {
        throw new UnsupportedOperationException(
                "method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException(
                "method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    /**
     * 服务暴露
     * @param arg0
     * @return
     * @throws org.apache.dubbo.rpc.RpcException
     */
    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) {
            throw new IllegalArgumentException(
                    "org.apache.dubbo.rpc.Invoker argument == null");
        }

        if (arg0.getUrl() == null) {
            throw new IllegalArgumentException(
                    "org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        }

        //NOTE: 根据URL的参数来确定具体要调用哪个Protocol的扩展实现类
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = ((url.getProtocol() == null) ? "dubbo" : url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                    "Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" +
                            url.toString() + ") use keys([protocol])");
        }

        //NOTE: 获得对应的Protocol的扩展实现类
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        //NOTE:服务暴露
        return extension.export(arg0);
    }

    /**
     * 服务引用
     * @param arg0
     * @param arg1
     * @return
     * @throws org.apache.dubbo.rpc.RpcException
     */
    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0,
                                              org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) {
            throw new IllegalArgumentException("url == null");
        }

        //NOTE: 根据URL的参数来确定具体要调用哪个Protocol的扩展实现类
        org.apache.dubbo.common.URL url = arg1;
        String extName = ((url.getProtocol() == null) ? "dubbo" : url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                    "Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" +
                            url.toString() + ") use keys([protocol])");
        }

        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        //引用服务
        return extension.refer(arg0, arg1);
    }
}

5、@Activate自**扩展

  • 入口:getActivateExtension(URL url …)
  • 对于集合类扩展,在使用时通过URL指定获取对应的扩展实现类,获取当前扩展的所有可自动**的实现。

6、实现AOP和IoC

  • 通过Wrapper类来“实现”AOP。扩展点自动包装
    • 什么是Wrapper类?(有一个持有扩展点类型的构造函数的扩展点实现类)
      • Wrapper类实现了扩展接口,但不是扩展点的真正实现。它从ExtensionLoader中返回的实际是Wrapper的实例,Wrapper持有实际扩展点的实现类。即Wrapper代理了扩展点,从而实现了AOP
  • 通过反射调用setter方法来“实现”IoC。扩展点自动装配
    • 在加载扩展点时,自动注入依赖的扩展点,加载扩展点时,扩展点实现类的成员如果为其他的扩展点类类型,ExtensionLoader会自动注入依赖的扩展点。
      • 实现方法: ExtensionLoader通过扫描扩展点实现类的所有setter方法来判断其成员,即ExtensionLoader会执行扩展点的拼装操作。
      • 存在的问题:在加载依赖扩展点时,如何确定要加载的是哪一个实现类?
//NOTE: 获得属性名,如setName方法,获得name。通过objectFactory(SpiExtensionFactory)获得依赖对象
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
    // NOTE: 调用setter方法设置依赖
    method.invoke(instance, object);
}

7、加载如Protocol等扩展点时,如何使用ExtensionLoader

  • 每次在ExtensionLoader.getExtensionLoader(Class clazz)时会根据扩展点遍历其对应"META-INF/dubbo/"路径下的所有配置的扩展实现类。
  • 这些扩展点的实现类统一由ExtensionFactory加载,而ExtensionFactory同样是一个扩展点,其Adaptive自适应扩展类是AdaptiveExtensionFactory。

大致加载逻辑

  • 加载某一个扩展点A时,首先会为ExtensionFactory创建ExtensionLoader,同时为ExtensionFactory的扩展点加载其自适应扩展类AdaptiveExtensionFactory,并加载扩展实现类SpiExtensionFactory。
  • 其次为扩展点A创建对应的ExtensionLoader,并创建自适应扩展类以及加载扩展实现类。(第一步创建的ExtensionFactory创建ExtensionLoader是作为“扩展点A”的objectFactory,用于后续属性注入)
  • 为什么加载每个扩展点都需要从ExtensionFactory这个扩展点开始?
    • 为了在后续创建扩展实现类对象时,对其内部进行属性赋值(即IoC属性注入),需要用到SpiExtensionFactory去创建依赖的扩展点实现类对象。

ExtensionLoader 更详细的使用,在后续更多dubbo源码解读时,会进一步分析,上述只是理解ExtensionLoader是如何工作,具有哪些功能的?

相关标签: ExtensionLoader SPI