avatar

43.Dubbo源码分析之核心篇(二)

上一篇我们简单的过了一下SPI的基本概念,以及简单使用了下Dubbo提供的一些SPI。
那么这篇我们将真正进入Dubbo的源码分析环节,也就是说通过分析Dubbo的SPI源码一步一步的进入Dubbo的内心世界。
因为SPI源代码很长,碍于篇幅原因可能会拆分成多份。
这一篇的内容如下:

  1. dubbo源码分析环境搭建
  2. ExtensionLoader.getExtesionLoader()源码分析

0x01 Dubbo源码分析环境搭建

  1. 事先说明,这里用来分析的Dubbo版本是2.7.2,工具用的是IDEA
  2. 首先我们从github上下载源码包。地址是dubbo.我们下载2.7.2版本。
  3. 解压zip包,然后打开IDEA,选择File->New->Project from existing Sources...
  4. 选择你解压的路径。然后点击ok,然后在弹出的对话框中选中import project from external modelmaven
  5. 选择Finish。搞定

0x02 从哪入手分析SPI?

  1. 环境我们搞好了,可以入手分析了,那么从哪里开始切入呢?还记得我们上一篇里面的demo么?我们加载扩展点的时候,第一行代码就是写它——ExtensionLoader.getExtensionLoader(xxxx.class);,那我们从getExtensionLoader()方法开始切入。
  2. ExtensionLoader.getExtensionLoader(xxxx.class)
    • 首先我们不看源码,从字面意思上看,它是获取一个类的ExtensionLoader.返回的是一个ExtensionLoader,然后我们就可以通过这个ExtensionLoader根据key来获取相应的实现类。那么come on。看看他内部吧。
    • 代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      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 an interface!");
      }
      if (!withExtensionAnnotation(type)) {
      throw new IllegalArgumentException("Extension type (" + type +
      ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
      }
      //这里是首先从EXTENSION_LOADERS中获取ExtensionLoader。
      //联系上下文来看,这个EXTENSION_LOADERS一定是个缓存。因为如果每次调用都去load一次文件,效率不高。
      //定义为:ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
      //先从缓存中get。缓存中没有再去new一个。
      ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
      if (loader == null) {
      //如果缓存中没有,就new一个。
      EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
      loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
      }
      return loader;
      }

      //ExtensionLoader的构造方法
      private ExtensionLoader(Class<?> type) {
      this.type = type;
      //这里我们可以看到是获取的AdaptiveExtension,这里我们后面回过头说。
      objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
      }
    • 从上面的代码来说,这个getExtensionLoader方法无法就是做了如下几件事:
      • 校验Class
      • 从缓存(concurrentHashMap)中get
      • 如果缓存中没有,就new一个出来。
    • 下面我们看看getExtension(String)方法吧。
  3. ExtensionLoader.getExtension()代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    public T getExtension(String name) {
    //name的判断
    if (StringUtils.isEmpty(name)) {
    throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
    return getDefaultExtension();
    }
    //获取一个Holder实例,Holder类的代码和getOrCreateHolder方法体见下方。
    Holder<Object> holder = getOrCreateHolder(name);
    //获取实例。
    Object instance = holder.get();
    //如果实例为空就调用createExtension创建一个。
    if (instance == null) {
    synchronized (holder) {
    //锁住后再get,判断一次,防止重复创建
    instance = holder.get();
    if (instance == null) {
    //创建实例
    instance = createExtension(name);
    holder.set(instance);
    }
    }
    }
    return (T) instance;
    }

    /**
    * Holder类代码
    */
    public class Holder<T> {

    private volatile T value;

    public void set(T value) {
    this.value = value;
    }

    public T get() {
    return value;
    }

    }

    //getOrCreateHolder()方法代码。从下面看,和ExtensionLoader处理一样,先从缓存拿,拿不到就new一个。
    private Holder<Object> getOrCreateHolder(String name) {
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
    cachedInstances.putIfAbsent(name, new Holder<>());
    holder = cachedInstances.get(name);
    }
    return holder;
    }
    • 从上面的代码来看,getExtension()做了如下几个事情:
      • key的合法性判断
      • 获取Holder,会先从缓存中获取,缓存中取不到就new一个Holder。
      • 从Holder中拿对应的对象,如果没有就调用createExtension()创建个。
  4. 很明显,createExtension()方法才是我们的重头戏。在使用的时候,我们应该会有一个疑问,我们配置的这个key-value文件是在什么时候读取的呢?它又是如何被加载的?里面还会有其他的功能吗?这一切的疑问,都将在createExtension()方法中为你解答。
    • 这部分代码,我打算先把里面的代码贴出来,然后逐步讲解。首先我们来总览一下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      private T createExtension(String name) {
      //(1)
      Class<?> clazz = getExtensionClasses().get(name);
      if (clazz == null) {
      throw findException(name);
      }
      try {
      //(2)
      T instance = (T) EXTENSION_INSTANCES.get(clazz);
      if (instance == null) {
      EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
      instance = (T) EXTENSION_INSTANCES.get(clazz);
      }
      //(3)
      injectExtension(instance);
      //(4)
      Set<Class<?>> wrapperClasses = cachedWrapperClasses;
      if (CollectionUtils.isNotEmpty(wrapperClasses)) {
      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 + ") couldn't be instantiated: " + t.getMessage(), t);
      }
      }
    • 如上所见,我会把代码分为4步进行说明。下面开始来看看标为(1)的部分。摘出来的代码如下:
      1
      2
      3
      4
      Class<?> clazz = getExtensionClasses().get(name);
      if (clazz == null) {
      throw findException(name);
      }
      • 从上面第一行代码来看,他是先调用getExtensionClasses()方法,获取一个东西(可能是个Map,我们暂不关心),然后直接通过传入的name就可以get到一个我们想要的Class。那么这时候我们就可以猜想一下:文件解析的操作是不是会在 getExtensionClasses()方法中完成呢?然后构建出一个Map,这样就可以让我们愉快的使用name直接get到一个Class了。代码如下:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        private Map<String, Class<?>> getExtensionClasses() {
        //cachedClasses一看就是一个缓存。先从缓存中get。缓存中get不到再解析配置。
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
        synchronized (cachedClasses) {
        classes = cachedClasses.get();
        if (classes == null) {
        //解析配置
        classes = loadExtensionClasses();
        cachedClasses.set(classes);
        }
        }
        }
        return classes;
        }
      • 上面代码逻辑很简单,解析配置会在loadExtensionClasses()中体现
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        private Map<String, Class<?>> loadExtensionClasses() {
        //提取并缓存默认扩展名(如果存在)。也就是缓存@SPI的value属性
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();

        //加载DUBBO_INTERNAL_DIRECTORY下面的文件,
        //type就是当前要加载的类,也就是我们ExtensionLoader.getExtensionLoader()方法所传递的类的全限定名
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

        //加载DUBBO_DIRECTORY下面的文件
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

        //加载SERVICES_DIRECTORY下面的文件
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

        return extensionClasses;
        }
      • 通过上面的代码,我们发现其实就加载了3个目录下面的文件,因为最开始的时候阿里还没有把dubbo捐apache。所以它的包名都是com.alibaba。所以才会有repace(“org.apache”,“com.alibaba”)的代码。那么是哪三个目录呢?
        • DUBBO_INTERNAL_DIRECTORY:META-INF/dubbo/internal/
        • DUBBO_DIRECTORY:META-INF/dubbo/
        • SERVICES_DIRECTORY:META-INF/services/
      • 接下来我们往里面分析loadDirectory()方法
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        //从传参可以看出,这里拼起来就类似`META-INF/dubbo/internal/top.zyzling.IHelloService`
        String fileName = dir + type;
        try {
        Enumeration<java.net.URL> urls;
        //获取当前的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()方法
      • loadResource()方法代码如下:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        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;
        int i = line.indexOf('=');
        if (i > 0) {
        // =号前面部分,也就是我们的key
        name = line.substring(0, i).trim();
        // =号后面部分,也就是我们的实现类的全限定名
        line = line.substring(i + 1).trim();
        }
        if (line.length() > 0) {
        //调用loadClass把实现类加载到内存中,解析这个类,并缓存取来
        //注意,在调这个方法的时候就已经通过Class.forName()把类加载到了内存
        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);
        }
        }
        • 上面的代码就是循环读取文件,利用=切割key和对应的实现类,最后调用loadClass()方法进行加载。
      • loadClass()方法代码如下:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        		private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        //判断该实现类的接口是不是我们getExtensionLoader()所传递的接口类。
        //也就是加载的实现类必须是要实现getExtensionLoader()所传递的接口类,否则报错。
        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.");
        }
        //判断在类上是否有加@Adaptive注解。这里后面讲到@Adaptive注解时再回过头来看一下。
        if (clazz.isAnnotationPresent(Adaptive.class)) {
        //todo something。等讲到@Adaptive注解时在回过头来看
        cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
        //如果是Warpper类,则缓存起来。怎样才是Warpper类呢?代码在下方
        //那么这里有什么用呢?等后面我们说到的时候再回过头来看
        cacheWrapperClass(clazz);
        } else {
        //到这里,说明上面的条件都不满足,
        clazz.getConstructor();
        //如果我们没有定义key。就使用实现类的simpleName小写
        if (StringUtils.isEmpty(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,也就是我们的key。如果配了多个,就需要缓存多个。
        String[] names = NAME_SEPARATOR.split(name);
        if (ArrayUtils.isNotEmpty(names)) {
        //缓存激活扩展点,等讲到激活扩展点时再回过头看
        cacheActivateClass(clazz, names[0]);
        for (String n : names) {
        //缓存key
        cacheName(clazz, n);
        //缓存类,会先从Map中get,没有才会缓存,有就会报错。实现在下方
        saveInExtensionClass(extensionClasses, clazz, name);
        }
        }
        }
        }

        //判断是否是Wrapper类。
        //判断方法就是看该实现类是否有一个参数为:getExtensionLoader()方法传递的接口类的构造方法。
        private boolean isWrapperClass(Class<?> clazz) {
        try {
        clazz.getConstructor(type);
        return true;
        } catch (NoSuchMethodException e) {
        return false;
        }
        }

        private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) {
        //先从Map中获取,如果没有就put,有就抛异常
        Class<?> c = extensionClasses.get(name);
        if (c == null) {
        extensionClasses.put(name, clazz);
        } else if (c != clazz) {
        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName());
        }
        }
        • 到这里我们整个getExtensionClasses()方法,算是从外到内全部分析完毕了。简单总结下吧,总共做了如下事情:
          - 首先从缓存中获取对应的Map,如果有就直接返回,没有的话就调用loadExtensionClasses加载.
          - 那么加载做了什么呢?首先会找到我们特定的目录,寻找下面以getExtensionLoader()方法的参数接口的全限定名为名的文件。
          - 然后就是解析文件了,通过=分割,把key和对应的实现类的全限定名取出来。
          - 使用Class.forName()加载实现类到内存。
          - 分析实现类,并缓存到Map中。
    • 到这里终于把(1)中的Class<?> clazz = getExtensionClasses().get(name);给搞完了。下面我们继续看(2)
      1
      2
      3
      4
      5
      6
      7
      //....以上代码省略
      T instance = (T) EXTENSION_INSTANCES.get(clazz);
      if (instance == null) {
      EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
      instance = (T) EXTENSION_INSTANCES.get(clazz);
      }
      //....以下代码省略
    • 上面的代码逻辑比较清晰和简单,就是先会根据我们getExtensionClass().getName(String)返回的Class对象去缓存中取实例。如果实例没有,就调用Class.newInstance()创建一个实例到缓存。
    • 继续,我们看(3),代码如下:
      1
      2
      3
      //....以上代码省略
      injectExtension(instance);
      //....以下代码省略
      • 从方法名上看,inject这个单词的中文翻译就是注入,看全方法名,意识是注入扩展。那么方法里的内容肯定是和注入相关了。dubbo的扩展点为什么需要注入?设想下,假设我有一个SPI扩展实现类,我里面的属性包含其他的SPI扩展的实现类,这时候是不是需要对其进行初始化?也就是说要注入。说起注入,我们就会想到Spring依赖注入,Spring提供了构造方法注入,set方法注入,属性注入。那么Dubbo是怎么实现的呢?一探究竟吧。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      private T injectExtension(T instance) {
      try {
      if (objectFactory != null) {
      //获取实例的所有方法,并遍历
      for (Method method : instance.getClass().getMethods()) {
      //如果方法是public setXXX
      if (isSetter(method)) {
      //判断是否有@DisableInject注解。如果有该注解,就跳过注入
      if (method.getAnnotation(DisableInject.class) != null) {
      continue;
      }
      //拿到第一个参数的类型,如果是基本数据类型或者String、Date、Number、Boolean等就跳过
      Class<?> pt = method.getParameterTypes()[0];
      if (ReflectUtils.isPrimitives(pt)) {
      continue;
      }
      try {
      //获取set后面的参数名。比如setVersion。那么property = version
      String property = getSetterProperty(method);
      //找到对应的扩展实现类,实现注入。
      Object object = objectFactory.getExtension(pt, property);
      if (object != null) {
      //调用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;
      }
      • 从上面代码看,dubbo SPI实现类的注入方式就明晓了,原来它是通过set方法进行注入。
    • 接下来就是createExtension()方法的最后一部分了。代码如下:
      1
      2
      3
      4
      5
      6
      Set<Class<?>> wrapperClasses = cachedWrapperClasses;
      if (CollectionUtils.isNotEmpty(wrapperClasses)) {
      for (Class<?> wrapperClass : wrapperClasses) {
      instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
      }
      }
    • 首先,这里是先获取一个cacheWrapperClasses,是一个Set,那么这个值是从哪里来的呢?还记得我们分析(1)部分时,最后面说的那几个后面碰到的时候才说的地方么?emm。来往上面找找。在loadClass()方法中,有这样的一个判断:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      	if(...){.....}
      else if (isWrapperClass(clazz)) {
      //如果是Warpper类,则缓存起来。怎样才是Warpper类呢?代码在下方
      //那么这里有什么用呢?等后面我们说到的时候再回过头来看
      cacheWrapperClass(clazz);
      }

      //判断是否是Wrapper类。
      //判断方法就是看该实现类是否有一个参数为:getExtensionLoader()方法传递的接口类的构造方法。
      private boolean isWrapperClass(Class<?> clazz) {
      try {
      //这里的type是我们ExtensionLoader.getExtensionLoader()传递的接口类
      clazz.getConstructor(type);
      return true;
      } catch (NoSuchMethodException e) {
      return false;
      }
      }
      //缓存WarpperClass
      private void cacheWrapperClass(Class<?> clazz) {
      if (cachedWrapperClasses == null) {
      cachedWrapperClasses = new ConcurrentHashSet<>();
      }
      cachedWrapperClasses.add(clazz);
      }
        - 这时候我我们来好好说说这段代码干了些啥事。首先我们要明确,WarpperClass的字面意思包装类,也就是把一个类包装起来,增强实现类的功能,这就是我们的装饰器模式。于是判断是否是WarpperClass的那段代码就好理解了。只要我这个实现类有一个构造方法的参数是接口类,就认为他是一个WarpperClass。因为我们包装的话,肯定是这个接口下的所有实现类都能包装。然后就是缓存啦。缓存到`cachedWrapperClasses`中,它是一个`ConcurrentHashSet`。
      
      • 回到现在(3)部分的代码。现在wrapperClass变量的值是什么,怎么来的我们都知道了。那么下面肯定是会对现在的实现类进行包装。如果有多个情况,会一个套一个的包装。这就类似于我们操作IO流,你经常会看到如下的代码:
        1
        InputStream inputStream = new BufferedInputStream(new FileInputStream("xxxx"))
        这就是装饰器模式。一个套一个。
      • 那么下面我们来看看他是如何包装的吧。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
        //遍历所有的WarpperClass
        for (Class<?> wrapperClass : wrapperClasses) {
        //这里有两步:
        //第一步是获取用接口类作为参数的构造函数,然后创建一个对象。
        //第二步是根据Set依赖注入
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }
        }
  5. 上面就是ExtensionLoader.getExtensionLoader()方法的全部内容了。这块内容很重要,是Dubbo最核心的源码之一。后面我们在分析服务注册、导出以及消费端RPC调用时会经常看到他们的身影。

0x03 总结

这里我们总结一下ExtensionLoader.getExtensionLoader()搞了写什么东东。

  1. 从缓存中获取Holder,如果没有就创建。然后调用get()获取实例。如果获取不到,则往下走。
  2. 从缓存中获取该接口所有的扩展类。如果获取不到,则往下走。
  3. 加载指定目录下面的以该接口全限定名命名的文件,通过=分割,得出我们的name(key)和实现类的全限定名。
  4. 通过Class.forName()方法把实现类加载到内存,然后判断该实现类的一些特性,做一些操作,比如我们已经说过的cacheWrapperClass。我们的实现类就是在这时候保存到缓存中去的。
  5. 这时候我们接口对应的文件中的类就装载到了内存。准确的说,除了一些特性的类,其他的普通的实现类都放到了我们第2步里面的缓存中。这时候再通过name去缓存中获取实现类,肯定是可以找到的(如果在对应的文件中配置了)。
  6. 通过Class从缓存中获取实例,如果获取不到就调用Class.newInstance()方法创建一个实例,并弄到缓存中。
  7. 如果我们实现类中有用到其他的SPI作为属性,这里就需要通过Setter方法进行依赖注入,把该SPI的实现类注入进来。这样我们在实现类中调用其他的SPI就不会有空指针出现了。
  8. 获取该接口的所有Warpper类,一个套一个的对该实现类进行包装。目的就是增强其功能。
  9. 经过上面一系列操作后,返回该实例,并且缓存起来。这样只要进程不停,下次就可以直接从缓存中get,而不用走这么多路。

好了,上面就是该篇的全部内容。才疏学浅,如有错误之处,还望不吝指教。谢谢观看~


评论