dubbo-dubbo spi详解

1. dubbo spi介绍

Dubbo 按照 SPI 配置文件的用途,将spi配置文件目录分成了三类目录:

  1. META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI
  2. META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件
  3. META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件。

然后Dubbo 将 SPI 配置文件改成了 KV 格式,例如:

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

中 key 被称为扩展名(也就是 ExtensionName),当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现。例如接口org.apache.dubbo.rpc.Protocol有几个实类,这里指定扩展名为 dubbo,Dubbo SPI 就知道我们要使用:org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类。使用 KV 格式的 SPI 配置文件的另一个好处是:让我们更容易定位到问题。假设我们使用的一个扩展实现类所在的 jar 包没有引入到项目中,那么 Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载。这些更加准确的异常信息降低了排查问题的难度,提高了排查问题的效率。

2. @SPI注解

dubbo 中某个接口被 @SPI注解修饰时,就表示该接口是扩展接口,如:

@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol {
}

相应配置文件为:

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

@SPI 注解的 value 值指定了默认的扩展名称,例如,在通过 Dubbo SPI 加载 Protocol 接口实现类时,如果SPI配置文件目录下org.apache.dubbo.rpc.Protocol文件中没有明确指定扩展名,则默认会将接口org.apache.dubbo.rpc.Protocol上 @SPI 注解的 value 值作为扩展名.以上为例即加载 dubbo 这个扩展名对应的 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类。

dubbo spi应用示例(配置文件和前面的一模一样,省略):

ExtensionLoader<Protocol> extensionLoader = ApplicationModel.defaultModel()
	.getDefaultModule().getExtensionLoader(Protocol.class);
Protocol protocol = extensionLoader.getExtension("dubbo");

ExtensionLoader的核心字段:

public class ExtensionLoader<T> {
	 /**
     * 缓存了扩展实现类与其实例对象的映射关系。在前文示例中,Key 为 Class,Value为接口实例对象
     * 如果: key: class org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
     *      value为DubboProtocol实例对象
     *
     */
    private final ConcurrentMap<Class<?>, Object> extensionInstances = new ConcurrentHashMap<>(64);
    /**
     * 当前ExtensionLoader实例负责加载的扩展spi接口class
     */
    private final Class<?> type;
    /**
     * 缓存了该ExtensionLoader实例对象加载的spi接口的所有的扩展实现类与扩展名之间的映射关系:
     * 如:   key                                                                 value
     *      {Class@1826} "class org.apache.dubbo.rpc.protocol.rest.RestProtocol" -> rest
     *      {Class@1835} "class org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol" -> injvm
     *      {Class@1828} "class org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol" -> registry
     *      {Class@1832} "class org.apache.dubbo.rpc.support.MockProtocol" -> mock
     *      {Class@1827} "class org.apache.dubbo.registry.integration.RegistryProtocol" -> service-discovery-registry
     *      {Class@1829} "class org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol" -> dubbo
     */
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    /**
     * 缓存了该 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
     /**
     * 缓存了该ExtensionLoader加载的扩展名与扩展实现对象Holder之间的映射关系:
     *  这里如: key                    value
     *          dubbo                  Holder实例对象(ProtocolSerializationWrapper<ProtocolFilterWrapper<ProtocolListenerWrapper<DubboProtocol>>>)
     */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    private volatile Class<?> cachedAdaptiveClass = null;
    /**
     * spi接口上@SPI注解的value值,也就是默认扩展名
     */
    private String cachedDefaultName;
     /**
     * LoadingStrategy接口有三个实现类都通过JDK SPI的方式加载,在dubbo-common项目的META_INF/services目录下创建了该接口的spi配置文件,
     * 文件中列出了实现的接口类,有如下三个:
     * org.apache.dubbo.common.extension.DubboInternalLoadingStrategy 加载META-INF/dubbo/internal/目录下的spi配置文件
     * org.apache.dubbo.common.extension.DubboLoadingStrategy   加载META-INF/dubbo/目录下的spi配置文件
     * org.apache.dubbo.common.extension.ServicesLoadingStrategy 兼容jdk,加载META-INF/services/目录下的配置文件
     */
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
}

ExtensionDirector#getExtensionLoader方法分析:

public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        checkDestroyed();
   if (type == null) {
       throw new IllegalArgumentException("Extension type == null");
   }
   if (!type.isInterface()) {
       throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
   }
   if (!withExtensionAnnotation(type)) {
       throw new IllegalArgumentException("Extension type (" + type +
           ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
   }
    // 1. find in local cache
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
    ExtensionScope scope = extensionScopeMap.get(type);
    if (scope == null) {
        SPI annotation = type.getAnnotation(SPI.class);
        scope = annotation.scope();
        extensionScopeMap.put(type, scope);
    }
    if (loader == null && scope == ExtensionScope.SELF) {
         // create an instance in self scope
         loader = createExtensionLoader0(type);
     }
     // 2. find in parent
     if (loader == null) {
         if (this.parent != null) {
             loader = this.parent.getExtensionLoader(type);
         }
     }
     // 3. create it
     if (loader == null) {
         loader = createExtensionLoader(type);
     }
     return loader;
 }
 private <T> ExtensionLoader<T> createExtensionLoader(Class<T> type) {
     ExtensionLoader<T> loader = null;
      if (isScopeMatched(type)) {
          // if scope is matched, just create it
          loader = createExtensionLoader0(type);
      }
      return loader;
  }
// 创建ExtensionLoader
private <T> ExtensionLoader<T> createExtensionLoader0(Class<T> type) {
        checkDestroyed();
    ExtensionLoader<T> loader;
    // 实例化ExtensionLoader,并放入map暂存
    extensionLoadersMap.putIfAbsent(type, new ExtensionLoader<T>(type, this, scopeModel));
    loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
    return loader;
}
// 构造方法
ExtensionLoader(Class<?> type, ExtensionDirector extensionDirector, ScopeModel scopeModel) {
    this.type = type;
    this.extensionDirector = extensionDirector;
    this.extensionPostProcessors = extensionDirector.getExtensionPostProcessors();
    initInstantiationStrategy();
    this.injector = (type == ExtensionInjector.class ? null : extensionDirector.getExtensionLoader(ExtensionInjector.class)
        .getAdaptiveExtension());
    this.activateComparator = new ActivateComparator(extensionDirector);
    this.scopeModel = scopeModel;
}

得到接口对应的 ExtensionLoader 对象之后会调用其 getExtension() 方法,根据传入的扩展名称返回相应的实例对象, ExtensionLoader#getExtension():

public T getExtension(String name) {
	// 核心方法
    T extension = getExtension(name, true);
    if (extension == null) {
        throw new IllegalArgumentException("Not find extension: " + name);
    }
    return extension;
}

public T getExtension(String name, boolean wrap) {
    checkDestroyed();
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    String cacheKey = name;
    if (!wrap) {
        cacheKey += "_origin";
    }
    // 实例对象封装在了Holder里面
    final Holder<Object> holder = getOrCreateHolder(cacheKey);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
            	// 执行创建扩展实例
                instance = createExtension(name, wrap);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

private T createExtension(String name, boolean wrap) {
     // 获取 cachedClasses 缓存,根据扩展名从cachedClasses缓存中获取扩展实现类Class
     // 如果 cachedClasses 未初始化,则会扫描前面介绍的三个 SPI 目录获取查找相应的 SPI 配置文件,然后加载其中的扩展实现类,
     // 最后将扩展名和扩展实现类的映射关系记录到 cachedClasses 缓存中。这部分逻辑在 loadExtensionClasses() 和 loadDirectory() 方法中。
     Class<?> clazz = getExtensionClasses().get(name);
     if (clazz == null || unacceptableExceptions.contains(name)) {
         throw findException(name);
     }
     try {
         // 从extensionInstances里查找
         T instance = (T) extensionInstances.get(clazz);
         // 查找失败
         if (instance == null) {
             // 根据反射创建
             extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
             instance = (T) extensionInstances.get(clazz);
             instance = postProcessBeforeInitialization(instance, name);
             injectExtension(instance);
             instance = postProcessAfterInitialization(instance, name);
         }

         // 自动包装
         if (wrap) {
             List<Class<?>> wrapperClassesList = new ArrayList<>();
             if (cachedWrapperClasses != null) {
                 wrapperClassesList.addAll(cachedWrapperClasses);
                 wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                 Collections.reverse(wrapperClassesList);
             }

             if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                 for (Class<?> wrapperClass : wrapperClassesList) {
                     Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                     boolean match = (wrapper == null) ||
                         ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                             !ArrayUtils.contains(wrapper.mismatches(), name));
                     if (match) {
                         instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                         instance = postProcessAfterInitialization(instance, name);
                     }
                 }
             }
         }

         // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
         initExtension(instance);
         return instance;
     } catch (Throwable t) {
         throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
             type + ") couldn't be instantiated: " + t.getMessage(), t);
     }
 }

3. JDK SPI 、dubbo spi 、spring spi的比较

JDK SPI: 每个扩展点单独一个文件;只能通过ServiceLoader的迭代器遍历获取扩展实现类,同时也会加载所有实现类,这样就会把用不到的类也会进行实例化,浪费一定资源;是java自带实现,不需要引入第三方依赖可直接使用.

dubbo spi:每个扩展点一个配置文件,文件名为接口的全限定名.支持别名的概念,可以通过别名获取某个具体扩展点的实现.配置文件的内容为key=value形式,key是别名,value是实现类的全限定名.

spring spi: spring指定配置文件为classpath下的META-INF/spring.factories, 所有的扩展点配置都放到这个文件中.配置文件内容为key=value的形式,key为接口的全限定名,value为实现类的全限定名,可以为多个.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值