美文网首页
1-dubbo源码分析之扩展机制

1-dubbo源码分析之扩展机制

作者: 致虑 | 来源:发表于2018-08-30 17:43 被阅读0次

    一.概览

    整体描述
    • dubbo利用spi扩展机制实现大量的动态扩展,要想充分了解dubbo的扩展机制,首先必须弄明白三个注解:
    • @SPI: 被此注解标记的接口,便是是可扩展的接口,实现类便是动态抓取的了
    • @Adaptive: 自适应扩展~ 该注解可以作用在“类”上,也可以作用在方法上,除了AdaptiveCompiler、AdaptiveExtensionFactory两个类之外,所有的都是注解在方法上。ExtensionLoader根据接口定义动态的生成适配器代码,并实例化这个生成的动态类。被Adaptive注解的方法会生成具体的方法实现。没备注街自然不能成为自适应了。
    • @Activate: 激活扩展~ 可以注解在需要注解在类上或者方法上,并注明被激活的条件及指明排序信息。使用场景最多的就是dubbo的拦截器这一块了。
    • 扩展核心类:ExtensionLoader 加载扩展点就是靠这个类了,加载方式后面详叙,先弄清楚几个主要的变量:
    • SERVICES_DIRECTORY: 定义SPI文件的扫描路径,dubbo源码中设置了好几个:META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/; 至于这些文件夹的文件格式如何看下图:


      image.png image.png

      文件名为需要扩展接口全路径,文件内容为自配key-->实现类的全路径,很简单;

    • EXTENSION_LOADERS: 拓展点加载器的缓存 --> 接口类型和ExtensionLoader实例的映射关系

    • EXTENSION_INSTANCES: 拓展点的缓存

    • cachedClasses: 扩展文件中的 key --> class

    • cachedInstances: 扩展文件中的 key --> Hold<反射出来的intance>

    • cachedDefaultName: 接口SPI默认的实现名(就是把接口上填的默认值), 这个扩展优先级最高就在这里体现了


    二.扩展点加载流程

    1-获取ExtensionLoader
    • 测试方法作为入口
        @Test
        public void testAdaptiveExtension() throws Exception {
            SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
    
            Map<String, String> map = new HashMap<String, String>();
            map.put("key", "impl_wxshi");
            URL url = new URL("p1", "1.2.3.4", 1010, "path", map);
    
            String echo = ext.yell(url, "haha");
            assertEquals("xxxxx", echo);
        }
    
    • 方法内第一行很明显就是获取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中
                */
               EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
               loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
           }
           return loader;
       }
    
    • 详细很简单:
    • 1.判断要获取的扩展接口是注解@SPI
    • 2.从缓存中获取EXTENSION_LOADERS,若没有直接创建并且放入缓存,即一类接口扩展点只需创建一次EXTENSION_LOADER
    • 3.返回EXTENSION_LOADER用于下一步getAdaptiveExtension();
    • 这里首次加载扩展点肯定会创建ExtensionLoader了,这里也是个很重要的点,跟着上面的代码EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));进入构造器:

      /**
      * 默认初始化一个ExtensionFactory,这个类是用来获取扩展点实体的,
      * adaptive就是通过这个类取得扩展点,从而将请求委派给合适的实体完成请求
      * Adaptive生成返回给用户后,用户对方法发起调用,adaptive通过getExtension取得provider。getExtension会通过cachedInstances判断此provider是否已cache。没有的话则通过createExtension生成。
      */
      private ExtensionLoader(Class<?> type) {
          this.type = type;
          /**
           * type如果是ExtensionFactory类型,那么objectFactory是null,否则是ExtensionFactory类型的适配器类型
           */
          objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
      }
      
    • 构造器里面又有一行这样的代码:ExtensionLoader.getExtensionLoader(ExtensionFactory.class);可想而知这里也是获取自适应点ExtensionFactory.class 用以构造ExtensionLoader了。

    • 看看ExtensionFactory的接口和一个主要实现:

    /**
     * ExtensionFactory自身也是个扩展点,它被SPI注解
     * 它有3个provider,分别是adaptive,spi和spring
     * ExtensionFactory是主要是用来加载被注入的类的实现,分为SpiExtensionFactory和SpringExtensionFactory两个,分别用来加载SPI扩展实现和Spring中bean的实现。
     */
    @SPI
    public interface ExtensionFactory {
    
        /**
         * Get extension.
         *
         * 获取指定类型的指定名称的扩展点
         *
         * @param type object type.
         * @param name object name.
         * @return object instance.
         */
        @Adaptive
        <T> T getExtension(Class<T> type, String name);
     }
    
    /**
     * AdaptiveExtensionFactory
     * AdaptiveExtesnionFactory是dubbo自定义的adaptive,由于聚合了SpringExtensionFactory和SpiExtensionFactory,从而能对需要注入的对象的所有set方法进行注入
     */
    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
    
        private final List<ExtensionFactory> factories;
    
        public AdaptiveExtensionFactory() {
            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);
        }
    
        @Override
        public <T> T getExtension(Class<T> type, String name) {
            for (ExtensionFactory factory : factories) {
                T extension = factory.getExtension(type, name);
                if (extension != null) {
                    return extension;
                }
            }
            return null;
        }
    }
    

    看见@SPI了,同时重点关注下 @Adaptive注解就明确了,这个注解在上面讲了,如果在类上,优先级最高,那取得自适应点就是这个AdaptiveExtensionFactory了,干的事情看后面的作用就知道了,获取扩展点的流程我们用自己的接口解释,这里基本都是一致的,继续往后划重点。


    2-自定义扩展点加载
    • 主要进入测试方法中的第一句后半截getAdaptiveExtension()方法,看下图注释

      /**
       * 对于扩展类型是ExtensionFactory的,设置为null
       * getAdaptiveExtension方法获取一个运行时自适应的扩展类型
       * 每个Extension只能有一个@Adaptive类型的实现,如果么有,dubbo会自动生成一个类
       * objectFactory是一个ExtensionFactory类型的属性,主要用于加载需要注入的类型的实现
       * 这里记住非ExtensionFactory类型的返回的都是一个AdaptiveExtensionFactory
       */
      @SuppressWarnings("unchecked")
      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;
      }
      

      这里就是创建自定义自适应扩展点的入口了,很简单

      • 1.从缓存中获取
      • 2.获取不到直接创建,再加入缓存
    • 直接进入 cachedAdaptiveInstance.set(instance);方法,创建扩展点

      /**
       * 获取到适配器类的Class,利用反射创建适配器类的实例
       * 如果需要自己声明一个adaptive,一定要有个无参构造方法。其实除了wrapper外的所有provider都必须要有个无参构造方法
       */
      @SuppressWarnings("unchecked")
      private T createAdaptiveExtension() {
          try {
              return injectExtension((T) getAdaptiveExtensionClass().newInstance());
          } catch (Exception e) {
              throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
          }
      }
      
    • 此处主要点就出现了:Class<?> getAdaptiveExtensionClass();

      private Class<?> getAdaptiveExtensionClass() {
          /**
           * 触发SPI流程的扫描
           */
          getExtensionClasses();
      
          /**
           * 如果通过上面的步骤可以获取到cachedAdaptiveClass直接返回,如果不行的话,就得考虑自己进行利用动态代理创建一个了
           * 什么情况下不为空?当扩展类上打上@Adaptive注解的时候,就会将这个类直接返回。如果没有上注解,怎么办,就得自己生成了,也就是createAdaptiveExtensionClass
           */
          if (cachedAdaptiveClass != null) {
              return cachedAdaptiveClass;
          }
      
          /**
           * 利用动态代理创建一个扩展类
           * 如果用户没有声明adaptive(cachedAdaptiveClass为空)则dubbo自动生成一个adaptive
           */
          return cachedAdaptiveClass = createAdaptiveExtensionClass();
      }
      

      看注释一目了然,继续跟踪getExtensionClasses(),该方法没有返回值,从前面那么多缓存就可以看出,这个必然是出发了各种相关缓存的动作,为后面return作用。

    • 继续看重点:SPI流程的扫描 getExtensionClasses();

      /** 当cachedClasses为空时则开始load spi classes */
      private Map<String, Class<?>> getExtensionClasses() {
          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();

      // synchronized in getExtensionClasses
      private Map<String, Class<?>> loadExtensionClasses() {
          /**
           * 获取到类型的SPI注解,所以利用SPI扩展点的地方,需要加入SPI注解
           */
          final SPI defaultAnnotation = type.getAnnotation(SPI.class);
          if (defaultAnnotation != null) {
              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));
                  }
                  /**
                   * 如果注解中有value,说明有默认的实现,那么将value放到cachedDefaultName中
                   */
                  if (names.length == 1) cachedDefaultName = names[0];
              }
          }
      
          /**
           * 开始load spi文件了,这是整个过程中最为关键也重要的一部
           * 从下面的地址中加在这个类型的数据的extensionClasses中,地址包括
           * META-INF/dubbo/internal/
           * META-INF/dubbo/
           * META-INF/services/
           */
          Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
          loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
          loadDirectory(extensionClasses, DUBBO_DIRECTORY);
          loadDirectory(extensionClasses, SERVICES_DIRECTORY);
          return extensionClasses;
      }
      

      该方法做了两件事:
      - 1.获取默认的注解值(即@SPI(XXX))中的这个XXX,并缓存;
      - 2.扫描默认路径,加载扩展点

      还记得概览中的三个文件路径吗,这个就是主要的配置扩展点的文件路径了,dubbo会全局扫描所有该路径,遍历加载扩展点。至于加载出来的优先级,最后当然比不上类上配置@Adaptive了,但如果指明了key,那优先级也就不管了。

    • 看loadDirectory(Map<String, Class<?>> extensionClasses, String dir)

      /**
       * load spi文件
       * dubbo spi的文件位置位于META-INF/dubbo/, META-INF/dubbo/internal和META-INF/services/,文件名就是接口class的full name
       * 对所有文件按行循环,读取的每行内容,以”=”号做分割,前面的是扩展点名称,后面的是实体处理类full name
       */
      private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
          String fileName = dir + type.getName();
          try {
              Enumeration<java.net.URL> urls;
              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);
          }
      }
      

      加载资源,获取urls,遍历解析扩展类,放入缓存,跟核心方法

    • loadResource(extensionClasses, classLoader, resourceURL);

       private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
           try {
               BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
               try {
                   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) {
                                   name = line.substring(0, i).trim();
                                   line = line.substring(i + 1).trim();
                               }
                               if (line.length() > 0) {
                                   /**
                                    *  Class.forName(line, true, classLoader)-->加载解析出来的类的全限定名称
                                    */
                                   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);
                           }
                       }
                   }
               } finally {
                   reader.close();
               }
           } catch (Throwable t) {
               logger.error("Exception when load extension class(interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t);
           }
      }
      
      

    无非就是解析配置项,获取扩展点类路径,通过类加载器加载,继续

    • loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);

      /** 开始进入loadAdaptive */
      private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
      
          /**
           * 判断是新加载clazz是否是type的子类,不是报错
           */
          if (!type.isAssignableFrom(clazz)) {
              throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
          }
          /**
           * 如果class上有@Adaptive,则是一个adaptive,此时开始下一个循环
           * 注意这个点,查询type类型适配器类的时候会优先寻找cachedAdaptiveClass,因为是系统指定的适配器类,优先级最高
           *
           */
          if (clazz.isAnnotationPresent(Adaptive.class)) {
              if (cachedAdaptiveClass == null) {
                  cachedAdaptiveClass = clazz;
              }
              /**
               * 多个adaptive类的实例,报错
               * 标准的适配器类只能有一个
               */
              else if (!cachedAdaptiveClass.equals(clazz)) {
                  throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
              }
          }
      
          /**
           * 如果不是则开始loadWrapper。判断wrapper的标准是class有没有一个参数为接口类型的构造参数。如果是wrapper则塞入到cachedWrapperClasses中
           * 不是wrapper时会抛出NoSuchMethodException异常,从而进入loadActivation和loadEntity的过程
           *
           * 如果这个扩展类没有打上@Adaptive注解就更有意思了。
           * 首先第一步会验证下有没有type这个类型作为入参的构造方法,为什么要这么做?因为Wrapper,有的类型需要包装一下,例如type=Protocol.class 就会看到有DubboProtocol真实的Protocal类
           * 还会有ProtocolFilterWrapper和ProtocolListenerWrapper这种Wrapper类,这种Wrapper类的共同点就是构造函数的入参是type类型,所以在解析的时候有这么一步
           * 如果有这种构造函数的就是Warpper类,将这些Warpper类型的数据放到cachedWrapperClasses这个集合中缓存
           * 如果没有这种类型的构造函数,就是正常的type类型的实例了
           *
           */
          else if (isWrapperClass(clazz)) {
              Set<Class<?>> wrappers = cachedWrapperClasses;
              if (wrappers == null) {
                  cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                  wrappers = cachedWrapperClasses;
              }
              /**
               * 将此wrapper放入wrappers容器里面,也就是cachedWrapperClasses里面
               */
              wrappers.add(clazz);
          }
      
      
          /** 进入loadActivation和loadEntity的过程 */
          else {
      
              /** 到了这里,说明是个正常的类 */
              clazz.getConstructor();
      
              /**
               * 1.处理name为null的情况,为空时截取provider的simpleName的一部分作为name。即:如果在文件中没有声明这个扩展的名称(=左边的部分),就会根据这个类名创建一个名称
               * 2.上面提到过dubbo从三个文件夹,其中包括META-INF/services。熟悉jdk spi的对这个应该不陌生,这是jdk spi的默认加载路径,但是jdk声明provider只要声明一个full name就行。通过这一步dubbo spi就可以兼容jdk语法的spi了。
               */
              if (name == null || name.length() == 0) {
                  name = findAnnotationName(clazz);
                  if (name.length() == 0) {
                      throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                  }
              }
      
              /**
               * 看下这个类上有没有Activate注解
               * 被@Activate声明的provider会被cache到cachedActivates里
               *
               *
               * 然后进入下一个环节@Activate数据的解析。
               * 1.查看type类上有没有@Activate注解,如果有的话,将名称与注解放到cachedActivates这个Map中进行缓存。
               * 2.将扩展类和名称放入cachedNames这个Map中进行缓存,将名称和扩展类的class放入传递进来的extensionClasses中
               * 3.最后这个extensionClasses会被返回出来被使用
               */
              String[] names = NAME_SEPARATOR.split(name);
              if (names != null && names.length > 0) {
                  Activate activate = clazz.getAnnotation(Activate.class);
      
                  //存在,就往cachedActivates里面添加名称与注解
                  if (activate != null) {
                      cachedActivates.put(names[0], activate);
                  }
                  for (String n : names) {
                      if (!cachedNames.containsKey(clazz)) {
                          cachedNames.put(clazz, n);
                      }
                      Class<?> c = extensionClasses.get(n);
                      /**
                       * 非wrapper和adaptive的class都会被put到extensionClasses里,它是个map,这个map会被返回,然后被cache到cachedClasses。
                       */
                      if (c == null) {
                          //最后往extensionClasses添加名称与class
                          extensionClasses.put(n, clazz);
                      } else if (c != clazz) {
                          throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                      }
                  }
              }
          }
      }
      

      注释很清楚,这里说明几个主要的点:

      • 1.判断当前类上是否有@Adaptive注解,如果有则记录下,因为毕竟次注解优先级最高,但是该注解既然是默认优先级,因此肯定只能存在一个,多了就不知道找谁了。
      • 2.是不是WrapperClass,从词义就知道这是一个包装类,其实类似于Spring的切面,将真正的扩展点类以构造方式包在其中,这样就可以实现一些额外逻辑了,即Wrapper 代理了扩展点。
      • 3.当然是解析正常类了,现获取类名,判断是否有@Activate注解,加入缓存等。
    • 到这里getExtensionClasses()就告一段落了,将刚才这里的出发点再贴一下,防止不能回头

     private Class<?> getAdaptiveExtensionClass() {
         /**
          * 触发SPI流程的扫描
          */
         getExtensionClasses();
    
         /**
          * 如果通过上面的步骤可以获取到cachedAdaptiveClass直接返回,如果不行的话,就得考虑自己进行利用动态代理创建一个了
          * 什么情况下不为空?当扩展类上打上@Adaptive注解的时候,就会将这个类直接返回。如果没有上注解,怎么办,就得自己生成了,也就是createAdaptiveExtensionClass
          */
         if (cachedAdaptiveClass != null) {
             return cachedAdaptiveClass;
         }
    
         /**
          * 利用动态代理创建一个扩展类
          * 如果用户没有声明adaptive(cachedAdaptiveClass为空)则dubbo自动生成一个adaptive
          */
         return cachedAdaptiveClass = createAdaptiveExtensionClass();
     }
    
    往下走就会发现,如果cachedAdaptiveClass != null,直接返回,这个就是体现优先级的地方了。若没有,继续深入createAdaptiveExtensionClass()
    
    • 利用动态代理创建一个扩展类

       /**
        * 将类以字符串的形式拼接出来,然后利用编译器进行编译,返回编译后的class对象
        */
       private Class<?> createAdaptiveExtensionClass() {
           /**
            * 创建代码的字符串形式
            */
           String code = createAdaptiveExtensionClassCode();
           /**
            * 寻找类加载器
            */
           ClassLoader classLoader = findClassLoader();
           /**
            * 寻找Compiler的适配器扩展类
            */
           com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
           /**
            * 进行编译成Class的实例返回
            */
           return compiler.compile(code, classLoader);
       }
      

      核心就在那个code的解释了,这里的code形式如下:

       package com.alibaba.dubbo.common.extensionloader.ext1;
       import com.alibaba.dubbo.common.extension.ExtensionLoader;
       
       public class SimpleExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt {
           
           public java.lang.String yell(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) {
               if (arg0 == null) throw new IllegalArgumentException("url == null");
               com.alibaba.dubbo.common.URL url = arg0;
               String extName = url.getParameter("key1", url.getParameter("key2", "impl1"));
               if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([key1, key2])");
               com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName);
               return extension.yell(arg0, arg1);
           }
           
           public java.lang.String echo(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) {
               if (arg0 == null) throw new IllegalArgumentException("url == null");
               com.alibaba.dubbo.common.URL url = arg0;
               String extName = url.getParameter("simple.ext", "impl1");
               if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([simple.ext])");
               com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName);
               return extension.echo(arg0, arg1);
           }
           public java.lang.String bang(com.alibaba.dubbo.common.URL arg0, int arg1) {
               throw new UnsupportedOperationException("method public abstract java.lang.String com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.bang(com.alibaba.dubbo.common.URL,int) of interface com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt is not adaptive method!");
           }
       }
      

    code还是字符串形式,那后面的代码就清楚了

    • 1.寻找类加载器

    • 2.再利用自适应扩展点(javassist)寻找编译器Compiler

    • 3.将字符串code生成具体的类,这个类就是最终的扩展点代理类啦。
      对于第二点为啥是javassist,看下图:

      /**
       * Compiler. (SPI, Singleton, ThreadSafe)
       */
      @SPI("javassist")
      public interface Compiler {
      
          /**
           * Compile java source code.
           *
           * @param code        Java source code
           * @param classLoader classloader
           * @return Compiled class
           */
          Class<?> compile(String code, ClassLoader classLoader);
      }
      

      该@SPI("javassist")应该足以说明默认取的是哪个代理器了。

    • 到此扩展点的代理就产生了,后面执行具体方法的时候,执行的实际是代理的方法体,再去执行具体逻辑,看字节码就清楚了执行过程。

    3.执行具体的方法
    • String echo = ext.echo(url, "haha");其实执行的代理的如下方法体,看上图字节码

       public java.lang.String echo(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) {
              if (arg0 == null) throw new IllegalArgumentException("url == null");
              com.alibaba.dubbo.common.URL url = arg0;
              String extName = url.getParameter("simple.ext", "impl1");
              if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([simple.ext])");
              com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName);
              return extension.echo(arg0, arg1);
      }
      

    看到执行逻辑如下:
    - 1.获取扩展点的Extensionloader,之前的流程中已经缓存了,所以直接获取
    - 2.调用getExtension(extName)方法获取具体的扩展点,这个就是最后获取真正代理的时候了

    • 获取实际扩展点的真正代理

      /**
       * 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 (name == null || name.length() == 0)
              throw new IllegalArgumentException("Extension name == null");
          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) {
              synchronized (holder) {
                  instance = holder.get();
                  if (instance == null) {
                      instance = createExtension(name);
      
                      /** 缓存 */
                      holder.set(instance);
                  }
              }
          }
          return (T) instance;
      }
      

      重点代码就是instance = createExtension(name); 返回instance,且缓存。继续跟踪:

      /**
       * 1.getExtensionClasses在cachedClasses有值时直接返回cachedClasses。上一节介绍过,loadFile结束后会把provider calss都cache到cachedClasses上
       * 2.通过cachedClasses就可以获得name对应的provider了
       * 3.国际惯例还是要inject
       * 4.存在wrapper的话则迭代包装,上一节也介绍过,先定义的包装后定义的。
       */
      @SuppressWarnings("unchecked")
      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 (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);
          }
      }
      

      风格完全不变,依然先尝试从缓存中获取,没有则EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
      这里就根据具体的class newInstance()出一个对象了,这个对象就是指定的具体的实际调用对象了。
      这里还有一个小的点,就是注释描述的那样,inject + wrapper,wrapper前面介绍过,inject如下:

      /**
       * 如果适配器类有属性的set方法,会自动注入
       * 生成provider后 通过injectExtension对其进行注入。注入是通过ExtensionFactory完成的
       * */
      private T injectExtension(T instance) {
          try {
              if (objectFactory != null) {
                  for (Method method : instance.getClass().getMethods()) {
                      if (method.getName().startsWith("set")
                              && method.getParameterTypes().length == 1
                              && Modifier.isPublic(method.getModifiers())) {
                          Class<?> pt = method.getParameterTypes()[0];
                          try {
                              String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                              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;
      }
      
    • 最后返回的instance就执行具体的方法。

    相关文章

      网友评论

          本文标题:1-dubbo源码分析之扩展机制

          本文链接:https://www.haomeiwen.com/subject/mcydwftx.html