2.从spi来开启dubbo的旅程

1. 写在前面:

​ 读dubbo源码之前建议先看看官方文档http://dubbo.apache.org/zh-cn/docs/user/quick-start.html,很少有人能比原作者们写的更好了,只能说是按自己的理解来阐述。

2. spi你想干啥子

​ 编程中我们有一般遵循几大原则比如开闭原则、单一职责、依赖倒置等等。依赖倒置核心思想是:要面向接口编程,不要面向实现编程。

​ SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。看到很多文章说SPI是java提供的一种机制,我认为不如说是jdk提供了SPI的一个具体实现,正如Spring与IOC、AOP的关系一样。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。

​ spring也有spi的实现,在自动注入查找接口实现类也需要用的这个能力,比如有多个实现类的时候不进行具体指定IOC容器会启动报错,但这不是本文的讨论重点,我们回到JDK和dubbo的实现上

3. jdk、dubbo你们家叫spi的闺女长得漂亮不

​ 例子的来源仍然是dubbo官网的源码分析部分,记录一下实现细节

3.1 配置准备

​ 在测试包下分别添加测试类与jdk(META-INF/services)、dubbo(META-INF/dubbo)的配置文件名为接口全类名,配置文件中记录实现类信息,测试类中获取具体实现。
在这里插入图片描述

jdk spi记录实现类全类名,路径:META-INF/services

com.example.productservice.service.OptimusPrime
com.example.productservice.service.Bumblebee

dubbo spi记录信息,路径:META-INF/dubbo

optimusPrime=com.example.productservice.service.OptimusPrime
bumblebee=com.example.productservice.service.Bumblebee

两者记录信息不同和具体实现有关系,下面是测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceApplicationTests {

    /**
     * jdk spi
     */
    @Test
    public void contextLoads() {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        System.out.println("Java SPI");
        serviceLoader.forEach(Robot::sayHello);
    }

    /**
     * dubbo spi
     */
    @Test
    public void sayHello() {
        ExtensionLoader<Robot> extensionLoader =
                ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

4. spi你过来,我们好好谈谈

​ 前面看过了spi都是些什么,接下来聊聊Dubbo的spi实现原理。其实最好的方式当然还是官方文档结合源码来看了http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html,如前文所描述下面的内容是笔者消化后打的饱嗝

4.1 从getExtension说起

我们首先根据官网文档找到SPI的入口org.apache.dubbo.common.extension.ExtensionLoader,下面的函数会加载给定的扩展,没找到就抛异常(注释说明)。整个代码的逻辑还是比较简单,首先检查缓存缓存未命中则创建拓展对象。

/**
 * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
 * will be thrown.
 */
@SuppressWarnings("unchecked")
public T getExtension(String name) {
    //校验入参合规
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    //
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

以上代码有3块封装的功能,getDefaultExtension(),getOrCreateHolder(String name),createExtension(String name)需要说明。前两者比较简单,创建扩展实例比较重要。

  • 先说getOrCreateHolder(String name),获取或创建holder,其实holder也很简单,只是统一持有了对象

    private Holder<Object> getOrCreateHolder(String name) {
        //缓存map(ConcurrentHashMap)中获取holder
        Holder<Object> holder = cachedInstances.get(name);
        //未获取到就new一个
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        return holder;
    }
    
    /**
     * Helper Class for hold a value.
     */
    public class Holder<T> {
        private volatile T value;
        public void set(T value) {
            this.value = value;
        }
        public T get() {
            return value;
        }
    }
    
  • 再说getOrCreateHolder(String name),它又有几个调用,不过逻辑上都很简单,我们一个个来看

    /**
      * Return default extension, return <code>null</code> if it's not configured.
      */
    public T getDefaultExtension() {
        //1.获取扩展classes
        getExtensionClasses();
        //2.如果没有配置,return null
    if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        //3.如果有,获取默认扩展
        return getExtension(cachedDefaultName);
    }
    
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    
    //获取扩展classes
    private Map<String, Class<?>> getExtensionClasses() {
        //cachedClasses在了上面,是一个持有String-Class类型k-v map的Holder
        Map<String, Class<?>> classes = cachedClasses.get();
        //双重检测,并发处理,加载扩展类
    if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                //加载扩展类
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
    private static final String SERVICES_DIRECTORY="META-INF/services/";
    private static final String DUBBO_DIRECTORY="META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY=DUBBO_DIRECTORY+"internal/";
    
    // synchronized in getExtensionClasses
    private Map<String, Class<?>> loadExtensionClasses() {
        //1. 加载缓存默认的扩展名
        cacheDefaultExtensionName();
    
        //2. 加载缓存配置的扩展名
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }
    
    /**
    * extract and cache default extension name if exists
      */
    private void cacheDefaultExtensionName() {
        // 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                // 对 SPI 注解内容进行切分
                String[] names = NAME_SEPARATOR.split(value);
                // 检测 SPI 注解内容是否合法,不合法则抛出异常
                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];
                }
            }
        }
    }
    
    //loadDirectory 方法先通过 classLoader 获取所有资源链接url,然后再通过 loadResource 方法加载资源。
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        // fileName = 文件夹路径 + type 全限定名 
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            //获取加载ExtensionLoader.class的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 occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t);
        }
    }
    
    //loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作
    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                // 按行读取配置内容
                while ((line = reader.readLine()) != null) {
                     // 定位 # 字符
                    final int ci = line.indexOf('#');
                    if (ci >= 0) {
                        // 截取 # 之前的字符串,# 之后的内容为注释,需要忽略
                        line = line.substring(0, ci);
                    }
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
                            // 以等于号 = 为界,截取键与值,这体现到了dubbo的spi配置
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                // 加载类,并通过 loadClass 方法对类进行缓存
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
    
    • loadClass我们单独来说

      //loadClass 方法用于主要用于操作缓存
      private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
          if (!type.isAssignableFrom(clazz)) {
              throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface.");
          }
          if (clazz.isAnnotationPresent(Adaptive.class)) {
              // 检测目标类上是否有 Adaptive 注解,完成缓存
              cacheAdaptiveClass(clazz);
          } else if (isWrapperClass(clazz)) {
              // 检测 clazz 是否是 Wrapper 类型,完成缓存
              cacheWrapperClass(clazz);
          } else {// 程序进入此分支,表明 clazz 是一个普通的拓展类
              // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
              clazz.getConstructor();
              if (StringUtils.isEmpty(name)) {
                  // 如果name为空,则尝试从Extension注解中获取 name,或使用小写的类名作为name
                  name = findAnnotationName(clazz);
                  if (name.length() == 0) {
                      throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                  }
              }
      		// 切分 name
              String[] names = NAME_SEPARATOR.split(name);
              if (ArrayUtils.isNotEmpty(names)) {
                  // 设置 cachedAdaptiveClass缓存
                  cacheActivateClass(clazz, names[0]);
                  for (String n : names) {
                      cacheName(clazz, n);
                      saveInExtensionClass(extensionClasses, clazz, name);
                  }
              }
          }
      }
      
      /**
       * cache Adaptive class which is annotated with <code>Adaptive</code>
       */
      private void cacheAdaptiveClass(Class<?> clazz) {
          if (cachedAdaptiveClass == null) {
              cachedAdaptiveClass = clazz;
          } else if (!cachedAdaptiveClass.equals(clazz)) {
              throw new IllegalStateException("More than 1 adaptive class found: "
      + cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName());
          }
      }
      
      /**
       * cache Activate class which is annotated with <code>Activate</code>
       * for compatibility, also cache class with old alibaba Activate annotation
       */
      private void cacheActivateClass(Class<?> clazz, String name) {
          Activate activate = clazz.getAnnotation(Activate.class);
          if (activate != null) {
              // 如果类上有 Activate 注解,则使用 names 数组的第一个元素作为键,
              // 存储 name 到 Activate 注解对象的映射关系
              cachedActivates.put(name, activate);
          } else {
              // support com.alibaba.dubbo.common.extension.Activate
              com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
              if (oldActivate != null) {
                  cachedActivates.put(name, oldActivate);
              }
          }
      }
      
      /**
       * cache wrapper class
       * like: ProtocolFilterWrapper, ProtocolListenerWrapper
       */
      private void cacheWrapperClass(Class<?> clazz) {
          if (cachedWrapperClasses == null) {
              cachedWrapperClasses = new ConcurrentHashSet<>();
          }
          cachedWrapperClasses.add(clazz);
      }
      
      /**
       * cache name
       */
      private void cacheName(Class<?> clazz, String name) {
          if (!cachedNames.containsKey(clazz)) {
              cachedNames.put(clazz, name);
          }
      }
      

      到这里整个默认extension的过程就完成了

  • 最后来看看 createExtension(String name),这里涉及一些dubbo IOC的内容,一起来看看

    private T createExtension(String name) {
        // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
        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);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                // 循环创建 Wrapper 实例
                for (Class<?> wrapperClass : wrapperClasses) {
                    //将当前instance作为参数传给Wrapper的构造方法,并通过反射创建 Wrapper 实例
                    //然后向Wrapper实例中注入依赖,最后将Wrapper实例再次赋值给 instance 变量
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));//完成包装与依赖注入
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
    
    
    
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                // 遍历目标类的所有方法
                for (Method method : instance.getClass().getMethods()) {
                    // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                    if (isSetter(method)) {
                        //检测是否关闭依赖注入
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }
                        // 获取 setter 方法参数类型
                        Class<?> pt = method.getParameterTypes()[0];
                        if (ReflectUtils.isPrimitives(pt)) {
                            continue;
                        }
                        try {
                            // 获取属性名,比如 setName 方法对应属性名 name
                            String property = getSetterProperty(method);
                            // 从 ObjectFactory 中获取依赖对象
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                // 通过反射调用 setter 方法设置依赖
                                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;
    }
    

    objectFactory的实现类SpiExtensionFactory 和 SpringExtensionFactory关于getExtension的实现并不复杂spring是通过context.getBean获取。spiExtension是通过缓存cachedAdaptiveInstance获取,获取不到就通过反射创建一个

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值