Dubbo 中的 SPI 机制(下)

简介: 本文主要讲述 Dubbo 中的 SPI 机制, 首先给大家带来一个简单的 Spring-Boot-Dubbo 案例,然后通过 Java 中的 SPI 的机制和讲解来引出 SPI 解决的问题,最后再通过一下几个方面讲述 Dubbo 中的 SPI 的设。 Dubbo 中的 SPI 实现 Dubbo 中的依赖注入 Dubbo 中的 AOP Dubbo 中的 Adaptive 机制 版本说明: dubbo 2.7.8

二. Dubbo 中的依赖注入


Dubbo 依赖注入和核心逻辑是通过 Set 方法进行依赖注入,会在 Dubbo SPI 或者 Spring SPI 中查找对象。


// 依赖注入
private T injectExtension(T instance) {
  if (objectFactory == null) {
    return instance;
  }
  try {
    for (Method method : instance.getClass().getMethods()) {
      if (!isSetter(method)) {
        continue;
      }
      if (method.getAnnotation(DisableInject.class) != null) {
        continue;
      }
      // 获取 set 方法参数类型
      Class<?> pt = method.getParameterTypes()[0];
      if (ReflectUtils.isPrimitives(pt)) {
        continue;
      }
      try {
        // 获取 setXXX 中的 xxx
        String property = getSetterProperty(method);
        Object object = objectFactory.getExtension(pt, property); // Car.class, car
        if (object != null) {
          // 根据参数类型或属性名,从 objectFactory 中获取到对象,然后调用 set 方法进行依赖注入
          method.invoke(instance, object);
        }
      } catch (Exception e) {
        logger.error("Failed to inject via method " + method.getName()
                     + " of interface " + type.getName() + ": " + e.getMessage(), e);
      }
    }
  } catch (Exception e) {
    logger.error(e.getMessage(), e);
  }
  return instance;
}
// ExtensionLoader
private ExtensionLoader(Class<?> type) {
  this.type = type;
  // ExtensionLoader 表示拓展类实例工程,可以利用  ExtensionFactory 得到某个拓展的对象实例
  // 或者得到 ExtensionFactory 接口的 AdaptiveExtensionFactory 实例, 利用 AdaptiveExtensionFactory 来获取某个类的实例
  objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
// AdaptiveExtensionFactory 构造方法
public AdaptiveExtensionFactory() {
  // 支持那些 ExtensionFactory (Spring, SPI)
  ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
  List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
  for (String name : loader.getSupportedExtensions()) {
    list.add(loader.getExtension(name));
  }
  factories = Collections.unmodifiableList(list);
}
// SpringExtensionFactory#getExtension 在 Spring 容器中去查找Bean
public <T> T getExtension(Class<T> type, String name) {
  // 如果有 @SPI 修饰就不管
  if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
    return null;
  }
  // 首先 byName, 然后 byType 
  for (ApplicationContext context : CONTEXTS) {
    T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
    if (bean != null) {
      return bean;
    }
  }
  return null;
}
// SpiExtensionFactory#getExtension
public <T> T getExtension(Class<T> type, String name) {
  // 接口上的 SPI 注解
  if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
    ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
    if (!loader.getSupportedExtensions().isEmpty()) {
      return loader.getAdaptiveExtension(); // 接口的 Adaptive 对象
    }
  }
  return null;
}
// ExtensionLoader
public T getAdaptiveExtension() {
  Object instance = cachedAdaptiveInstance.get();
  if (instance == null) {
    if (createAdaptiveInstanceError != null) {
      throw new IllegalStateException("Failed to create adaptive instance: " +
                                      createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
    }
    synchronized (cachedAdaptiveInstance) {
      instance = cachedAdaptiveInstance.get();
      if (instance == null) {
        try {
          // 如果不存在就创建
          instance = createAdaptiveExtension();
          cachedAdaptiveInstance.set(instance);
        } catch (Throwable t) {
          createAdaptiveInstanceError = t;
          throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
        }
      }
    }
  }
  return (T) instance;
}
// 获取 AdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
  // 获取当前接口的所有拓展类
  getExtensionClasses();
  // 缓存了 @Adaptive 注解标记类
  if (cachedAdaptiveClass != null) {
    return cachedAdaptiveClass;
  }
  // 如果某个接口没有手动指定 Adaptive, 那么就生成一个 Adaptive 类
  return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
// 创建代理类
private Class<?> createAdaptiveExtensionClass() {
  // 生成代码
  String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
  ClassLoader classLoader = findClassLoader();
  org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
  return compiler.compile(code, classLoader);
}


三. Dubbo 中的 AOP


dubbo中也实现了一套非常简单的 AOP,就是利用 Wrapper.class ,如果一个接口的扩展点中包含了多个 Wrapper 类,那么在实例化完某个扩展点后,就会利用这些 Wrapper 类对这个实例进行包裹,比如:现在有一个 DubboProtocol 的实例,同时对于 Protocol 这个接口还有很多的 Wrapper,比如 ProtocolFilterWrapper、ProtocolListenerWrapper,那么,当对 DubboProtocol 的实例完成了IOC之后,就会先调用 new ProtocolFilterWrapper(DubboProtocol实例) 生成一个新的Protocol的实例,再对此实例进行IOC,完了之后,会再调用new ProtocolListenerWrapper(ProtocolFilterWrapper实例)生成一个新的Protocol的实例,然后进行IOC,从而完成DubboProtocol实例的AOP。


// ExtensionLoader#createExtension
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);
      if (wrapper == null
          || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
      }
    }
  }


四. Dubbo 中的 Adaptive 机制


每个扩展点都有一个name,通过这个name可以获得该name对应的扩展点实例,但是有的场景下,希望一次性获得多个扩展点实例,可以通过传入多个name来获取,可以通过识别URL上的信息来获取:


extensionLoader.getActivateExtension(url, new String[]{"car1", "car2"}); 这个可以拿到name为car1和car2的扩展类实例,同时还会通过传入的url寻找可用的扩展类,  怎么找的呢?


在一个扩展点类上,可以添加 @Activate 注解,这个注解的属性有:


  1. String[] group():表示这个扩展点是属于拿组的,这里组通常分为 PROVIDERCONSUMER,表示该扩展点能在服务提供者端,或者消费端使用


  1. String[] value():指示的是URL中的某个参数 key,当利用 getActivateExtension 方法来寻找扩展点时,如果传入的url中包含的参数的所有 key 中,包括了当前扩展点中的 value 值,那么则表示当前 url 可以使用该扩展。
相关文章
|
存储 负载均衡 Dubbo
深入理解Dubbo-4.Dubbo扩展SPI
深入理解Dubbo-4.Dubbo扩展SPI
198 1
|
Dubbo Java 应用服务中间件
Dubbo服务暴露机制解密:深入探讨服务提供者的奥秘【九】
Dubbo服务暴露机制解密:深入探讨服务提供者的奥秘【九】
156 0
|
缓存 Dubbo Java
Dubbo 第三节_ Dubbo的可扩展机制SPI源码解析
Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。Protocol是⼀个接。但是,不是只要在⽅法上加了。
171 0
|
10月前
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
设计模式 JSON Dubbo
超越接口:探索Dubbo的泛化调用机制
超越接口:探索Dubbo的泛化调用机制
1013 0
|
Dubbo 网络协议 应用服务中间件
分布式微服务框架dubbo原理与机制
分布式微服务框架dubbo原理与机制
203 0
|
XML 缓存 Dubbo
Dubbo的魔法之门:深入解析SPI扩展机制【八】
Dubbo的魔法之门:深入解析SPI扩展机制【八】
195 0
|
XML 负载均衡 Dubbo
了解Dubbo配置:优先级、重试和容错机制的秘密【五】
了解Dubbo配置:优先级、重试和容错机制的秘密【五】
680 0
|
Dubbo Java 应用服务中间件
微服务框架(十五)Dubbo 超时机制及服务降级
此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。 本文为Dubbo超时机制及服务降级 当服务出现创建超时的时候,TimeoutFilter会打印该创建记录的详细信息,日志级别为WARN,即为可恢复异常,或瞬时的状态不一致