Dubbo源码通~ExtensionLoader

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初始化示意图
    在这里插入图片描述

  • 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是如何工作,具有哪些功能的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只打杂的码农

你的鼓励是对我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值