乐趣区

关于java:dubbo-SPI-实现详解

咱们晓得 dubbo 的扩展性十分强,扩大点十分多,这些扩大都是靠 SPI 来加载的,然而 dubbo 并没有应用规范的 SPI 而是抉择了本人实现,这又是为什么呢?

JDK SPI 机制

jdk 的 SPI 设计是为了能够面向接口编程,使第三方能够更好的对接口进行扩大实现,而应用方无需指定实现类,合乎软件设计的可插拔准则,典型的应用场景如 jdbc,咱们晓得在应用数据库时咱们只须要将对应数据库的驱动包引入即可,无需额定编码,用过 SLF4J 的同学必定也晓得我的项目中放一个实现了 SLF4J 的对应接口的实现就能够间接应用了,无需编码指定具体实现类,这就是 SPI 的功绩。但很多组件都没有基于原始的 SPI 做扩大而是抉择在 SPI 的原理根底上本人实现,起因是 JDK 原生的 SPI 有一些局限性。

JDK 的 SPI 实现有以下问题:

  1. 会默认实例化所有扩大,不能按需加载,如果扩大多而且大多数又用不到甚至某些还比拟消耗资源或者加载比较慢就会白白浪费资源浪费时间
  2. 获取扩大时只能通过 iterator 的形式获取扩大,不能用 key 获取,查找时不太不便
  3. 扩大加载失败时提示信息不敌对,甚至找不到是哪个扩大加载失败了

dubbo SPI 机制

dubbo 官网也写了其 SPI 的实现机制,总结下能够失去如下信息:

  1. dubbo 的 SPI 是按需加载,不会初始化所有扩大类
  2. 每个扩大能够有一个名字,加载失败时能够依据名字找到具体的加载失败扩展名,获取扩大时也能依据名称获取,方便使用
  3. 减少了对扩大点 IoC 和 AOP 的反对,一个扩大实现能够通过 setter 注入到其余扩大实现,还有 Wrapper 实现会主动包装最终实现类,且会主动通过结构器注入理论实现类,ExtensionLoader 在加载扩大点时,如果加载到的扩大点有拷贝构造函数,则断定为扩大点 Wrapper 类。
  4. 动静扩大自适应、主动激活机制,即被注解 @Adaptive 的办法会依据注解中的 key 运行时适配其实现类,主动激活是指被 @Activate 标记的实现类反对自定义加载条件

dubbo 的许多模块都和 SPI 密切相关,了解 dubbo 的 SPI 实现机制对浏览 dubbo 源码会有很大助益;上面从一些外围注解和类逐个进行解说。

注解 @SPI

这个注解用在接口类上,示意其是一个可扩大类,dubbo 的 SPI 实现会在获取扩大实现是查看接口类是否带有该注解,否则报错;该注解反对设定默认扩大名称,如 dubbo 的 Protocol 接口指定了 dubbo 为其默认实现类,那 dubbo 这个 key 又是怎么来的呢,咱们来看看 dubbo 扩大的配置格局,在 DubboProtocol 类的模块下 META-INF/dubbo/internal 目录中有一个 com.alibaba.dubbo.rpc.Protocol 文件,他是以扩大实现的接口名命名的,文件中的内容为 dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol,能够看到它是一个 key=value 的格局,dubbo 这个 key 即代表了 value 这个实现类。

注解 @Adaptive

该注解代表其是一个自适应接口或者类,该注解能够用在接口类或者办法上,示意办法或接口为自适应接口,用在接口类上示意该类的所有办法都为自适应办法,Adaptive 注解提供了一个能够运行时动静指定实现类的机制,以 Transporter 为例:

@SPI("netty") // 默认实现类为 netty
public interface Transporter {
    /**
     * 如果 Adaptive 注解中有参数则依照 Adaptive 的参数顺次查找 url,优先级为 Constants.SERVER_KEY、Constants.TRANSPORTER_KEY、netty
     * 体现在应用形式上就是
     * 生成的代码为 url.getParameter("server", url.getParameter("transporter", "netty")); 
     * 更具体的解释:1. 查找 url 中参数 server 为 key 的 value,如果有则以此为实现类,否则持续查找 transporter key 的 value,如果仍然不存在则以 netty 为 value 的默认值,而默认值 netty 来自于 @SPI("netty") 注解
    */
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

该注解还能够用在具体的实现类上,如 AdaptiveExtensionFactory 表明该类是一个适配器实现类,当应用getAdaptiveExtension 办法时会优先返回该类而不再应用编译器动静生成适配器类XXX$Adpative

注解 @Activate

指定该扩大的应用条件,默认即所有状况下都加载,典型场景是 dubbo 的 Filter,其应用形式为 ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, "provider|consumer"),其返回的是一个 扩大类列表。

接口 ExtensionFactory

指定扩大获取的形式,值得一提的是 ExtensionFactory 自身也是带有 @SPI 注解的,也就是说他是依赖 dubbo 自身的 SPI 实现的。该接口一共有 3 个实现类:

  • SpiExtensionFactory 通过 dubbo SPI 机制获取扩大
  • SpringExtensionFactory 通过 Spring IOC 获取扩大
  • AdaptiveExtensionFactory ExtensionFactory 的适配器类,应用 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()办法时返回该类

Wrapper 机制

ExtensionLoader 在加载扩大点时,如果加载到的扩大点的构造函数是以该扩大点的接口为入参的,则断定为扩大点 Wrapper 类。如 MockClusterWrapper 其结构器为

public class MockClusterWrapper implements Cluster {
    // 典型的 Wrapper 结构器
    public MockClusterWrapper(Cluster cluster) {this.cluster = cluster;}
}

Wrapper 类的用途是 SPI 在返回具体的扩大实现类时会用 Wrapper 类包装一下再返回,返回的实际上是一个 Wrapper 类的实例,同一个接口的 Wrapper 类能够有多个,会层层包装再返回,Wrapper 类的作用是将一些公共逻辑放到 Wrapper 类中进行复用,其实就是一个装璜器模式加强,dubbo 中大量应用装璜器模式进行编码,以此实现层次化设计,代码的复用性很高

主动注入

dubbo 用主动注入的形式实现了扩大类之间相互依赖的问题,在创立扩大实现类实例后会对其 setter 办法进行扫描,如果办法参数类型是扩大类型就调用反射进行 setter 注入

注解 @DisableInject

该注解顾名思义是对一些不心愿主动注入的属性进行标记,用在 setter 办法上

ExtensionLoader

dubbo 的 SPI 实现都在 ExtensionLoader 这个类中,代码足足有一千多行,上面咱们从源码上来看看它是怎么实现的。

  • public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)

    /**
     * 获取一个接口的 ExtensionLoader 实例,每个扩大类都会创立一个 ExtensionLoader 实例
     * 要获取一个扩大接口的相干信息都须要先获取  ExtensionLoader
    */
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
      // 判空
      if (type == null)
          throw new IllegalArgumentException("Extension type == null");
      // 必须是接口类
      if (!type.isInterface()) {throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
      }
      // 必须被 @SPI 注解
      if (!withExtensionAnnotation(type)) {
          throw new IllegalArgumentException("Extension type(" + type +
                  ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + "Annotation!");
      }
      // 先查缓存
      ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
      // 缓存不存在则创立
      if (loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
          loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
      }
      return loader;
    }
  • public T getExtension(String name)

    /**
     * 依据扩展名获取扩大实现类实例
     */
     public T getExtension(String name) {if (name == null || name.length() == 0)
          throw new IllegalArgumentException("Extension name == null");
      // 查找默认扩大实现,也就是 @SPI("默认值")
      if ("true".equals(name)) {return getDefaultExtension();
      }
      // 先查缓存
      Holder<Object> holder = cachedInstances.get(name);
      if (holder == null) {
          // 缓存不存在就创立
          cachedInstances.putIfAbsent(name, new Holder<Object>());
          holder = cachedInstances.get(name);
      }
      Object instance = holder.get();
      if (instance == null) {
          // holder 的意义就是用来上锁
          synchronized (holder) {instance = holder.get();
              if (instance == null) {
                  // 实在创立具体实现类逻辑
                  instance = createExtension(name);
                  // 放入缓存
                  holder.set(instance);
              }
          }
      }
      return (T) instance;
    }
  • private T createExtension(String name)

    /**
     * 创立扩大类实例
     */
    private T createExtension(String name) {
      // 取得对应的具体实现类,getExtensionClasses 扫描 META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/ 文件夹查找扩大配置
      Class<?> clazz = getExtensionClasses().get(name);
      if (clazz == null) {throw findException(name);
      }
      try {
          // 现看缓存
          T instance = (T) EXTENSION_INSTANCES.get(clazz);
          if (instance == null) {
              // 缓存不存在就创立
              EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
              instance = (T) EXTENSION_INSTANCES.get(clazz);
          }
          // 主动注入依赖的其余扩大类
          injectExtension(instance);
          // 主动包装,用 Wrapper 类包装,循环层层嵌套包装
          Set<Class<?>> wrapperClasses = cachedWrapperClasses;
          if (wrapperClasses != null && !wrapperClasses.isEmpty()) {for (Class<?> wrapperClass : wrapperClasses) {instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
              }
          }
          return instance;
      } catch (Throwable t) {
          throw new IllegalStateException("Extension instance(name:" + name + ", class:" +
                  type + ")  could not be instantiated:" + t.getMessage(), t);
      }
    }
  • private Map<String, Class<?>> loadExtensionClasses()

    /**
     * 从三个配置文件夹中扫描以后 ExtensionLoader 实例对应的接口类的实现类配置
     */
    private Map<String, Class<?>> loadExtensionClasses() {final SPI defaultAnnotation = type.getAnnotation(SPI.class);
      if (defaultAnnotation != null) {
          //@SPI 内的默认值
          String value = defaultAnnotation.value();
          if ((value = value.trim()).length() > 0) {String[] names = NAME_SEPARATOR.split(value);
              // 只容许有一个默认值
              if (names.length > 1) {throw new IllegalStateException("more than 1 default extension name on extension" + type.getName()
                          + ":" + Arrays.toString(names));
              }
              if (names.length == 1) cachedDefaultName = names[0];
          }
      }
      // 扫描 3 个文件夹
      Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
      loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
      loadDirectory(extensionClasses, DUBBO_DIRECTORY);
      loadDirectory(extensionClasses, SERVICES_DIRECTORY);
      return extensionClasses;
    }
  • private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir)

    /**
     * 扫描一个文件夹获取其中的接口实现类配置
     */
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
      // 拼接文件名
      String fileName = dir + type.getName();
      try {
          Enumeration<java.net.URL> urls;
          // 用以后 ExtensionLoader 的 classloader 扫描加载扩大实现类配置
          ClassLoader classLoader = findClassLoader();
          if (classLoader != null) {urls = classLoader.getResources(fileName);
          } else {urls = ClassLoader.getSystemResources(fileName);
          }
          if (urls != null) {
              // 遍历每个文件
              while (urls.hasMoreElements()) {java.net.URL resourceURL = urls.nextElement();
                  // 解析文件内容,加载对应的扩大类,就不开展讲了,比较简单
                  loadResource(extensionClasses, classLoader, resourceURL);
              }
          }
      } catch (Throwable t) {
          logger.error("Exception when load extension class(interface:" +
                  type + ", description file:" + fileName + ").", t);
      }
    }
  • private T injectExtension(T instance)

    /**
     * 如果以后扩大类实例依赖了其余的扩大类则用 setter 办法注入对应的实例
     */
    private T injectExtension(T instance) {
      try {if (objectFactory != null) {
              // 反射取得该类的所有办法
              for (Method method : instance.getClass().getMethods()) {
                  // 是 set 办法且只有一个参数且申明为 public
                  if (method.getName().startsWith("set")
                          && method.getParameterTypes().length == 1
                          && Modifier.isPublic(method.getModifiers())) {
                      // 是否禁止了主动注入
                      if (method.getAnnotation(DisableInject.class) != null) {continue;}
                      Class<?> pt = method.getParameterTypes()[0];
                      try {
                          // 截取 setter 办法的后半局部作为属性名称 比方:setProtocol 截取进去是 protocol
                          String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                          // 调用 objectFactory 尝试获取扩大实例
                          Object object = objectFactory.getExtension(pt, property);
                          if (object != null) {
                              // 调用反射注入属性
                              method.invoke(instance, object);
                          }
                      } catch (Exception e) {logger.error("fail to inject via method" + method.getName()
                                  + "of interface" + type.getName() + ":" + e.getMessage(), e);
                      }
                  }
              }
          }
      } catch (Exception e) {logger.error(e.getMessage(), e);
      }
      return instance;
    }
  • public T getAdaptiveExtension()

    /**
     * 获取一个自适应扩大实例,该实例自身并不实现扩大接口,会在运行时动静从 url 中获取实现类的 name 再加载对应的实现类返回
     */
    public T getAdaptiveExtension() {
      // 先查缓存
      Object instance = cachedAdaptiveInstance.get();
      if (instance == null) {
          // 是否已经创立过失败了
          if (createAdaptiveInstanceError == null) {synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();
                  if (instance == null) {
                      try {
                          // 创立适配器对象
                          instance = createAdaptiveExtension();
                          cachedAdaptiveInstance.set(instance);
                      } catch (Throwable t) {
                          createAdaptiveInstanceError = t;
                          throw new IllegalStateException("fail to create adaptive instance:" + t.toString(), t);
                      }
                  }
              }
          } else {throw new IllegalStateException("fail to create adaptive instance:" + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
          }
      }
    
      return (T) instance;
    }
  • private Class<?> getAdaptiveExtensionClass()

    /**
     * 获取适配器类,每个含有 @Adaptive 注解的类或办法都会有这个类,这个类是字节码动静生成的,* 例:com.alibaba.dubbo.rpc.cluster.Cluster$Adaptive
     */
    private Class<?> getAdaptiveExtensionClass() {
      // 确定扫描过了 SPI 配置文件
      getExtensionClasses();
      // 先查缓存,这里留神如果以后接口有实现类被 @Adaptive 注解标记了则会扫描时放进缓存,不会走字节码生成过程
      // 如:AdaptiveCompiler
      if (cachedAdaptiveClass != null) {return cachedAdaptiveClass;}
      // 动静生成适配器类
      return cachedAdaptiveClass = createAdaptiveExtensionClass();}
  • private Class<?> createAdaptiveExtensionClass()

    /**
     * 用编译器生成适配类的代码并加载该类
     */
    private Class<?> createAdaptiveExtensionClass() {
      // 生成适配器类源代码
      String code = createAdaptiveExtensionClassCode();
      ClassLoader classLoader = findClassLoader();
      // 这里相当于递归调用了 getAdaptiveExtension,但因为 Compiler 有动态实现类 AdaptiveCompiler,不会走到这里来,所以不会呈现有限递归
      com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
      // 编译并加载
      return compiler.compile(code, classLoader);
    }
  • public List<T> getActivateExtension(URL url, String key) 获取无条件加载的扩大实现类,即用了 Activate 注解且注解中 group 参数未设置的扩大类
  • public List<T> getActivateExtension(URL url, String key, String group) 获取有加载条件的扩大实现类,即用了 Activate 注解且注解中 group 参数不为空的扩大类
  • public List<T> getActivateExtension(URL url, String[] values, String group) 上边两个办法都是调用改办法实现的
    该办法会将多个扩大类依据用户配置的内容进行增减及排序,典型场景:过滤器,排序是用 ActivateComparator 来实现的
退出移动版