dubbo源码分析 -- dubbo的SPI机制

概念

在dubbo的整体结构中,除了service层和config层,其他各层都支持自定义扩展。而自定义扩展的实现,就是dubbo实现的一套类似SPI的扩展点加载机制。在这个机制中,ExtensionLoader是核心类。

组件

ExtensionLoader

dubbo SPI机制的主要使用入口,基本就是通过这个类来使用和实现SPI机制的

ExtensionFactory

dubbo的SPI机制,具备Ioc的功能特性。其实现主要由ExtensionFactory支持。大概的逻辑就是,如果某个组件具有setXX方法,则会通过ExtensionFactory为其注入依赖。ExtensionFactory在dubbo中,就类似与spring的BeanFactory,算是一种容器。

接口定义

@SPI//本身也支持dubbo的扩展点机制
public interface ExtensionFactory {
  //获取扩展类
  <T> T getExtension(Class<T> type, String name);
}

子类

  • SpringExtensionFactory:关联了spring的ApplicationContext
  • SpiExtensionFactory:通过dubbo自己的SPI机制,读取指定目录下的文件
  • AdaptiveExtensionFactory:通过所有别的ExtensionFactory来实现功能。由于有@Adaptive注解,所以这个肯定是ExtensionFactory具体使用的实现类

用法

在dubbo中,扩展点加载有三个核心注解:@SPI、@Adaptive、@Activate

@SPI

用在接口上,标记一个接口是dubbo的SPI接口,可以通过dubbo的SPI机制记载。

@Adaptive

用在具体的实现类上

标识接口的默认实现就是这个标注了@Adaptive的实现类。比如ExtensionFactory接口就有一个@Adaptive标注的实现类:AdaptiveExtensionFactory。所以,ExtensionFactory的默认实现就是AdaptiveExtensionFactory。一个接口最多只能有一个实现类标注了@Adaptive注解,否则会注解

用在接口的方法上

表示,会根据方法参数在运行时动态匹配到一个具体的实现类

@Activate

标注在具体的实现类上,表示这个实现类在某些条件下生效。

注解定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
	//根据group参数匹配
  String[] group() default {};
	//根据url中是否有指定的key匹配
  String[] value() default {};
	//排序相关
  String[] before() default {};
  String[] after() default {};
  int order() default 0;
}

实现

ExtensionLoader的入口方法有三个:

  • getExtension:获取普通的扩展类
  • getActivateExtension:获取activate扩展类
  • getAdaptiveExtension:获取adaptive扩展类

0.加载dubbo SPI配置文件

加载配置文件,是几个入口公用的功能,先拿出来说一下。

//加上缓存机制,具体的加载是loadExtensionClasses()方法实现
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;
}

//加载spi的配置
private Map<String, Class<?>> loadExtensionClasses() {
  //读取类上的@SPI注解
  final SPI defaultAnnotation = type.getAnnotation(SPI.class);
  if (defaultAnnotation != null) {
    String value = defaultAnnotation.value();
    //@SPI注解的value值就是这个SPI扩展点的默认实现类的key,只能有一个
    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];
    }
  }

  Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
  //读取META-INF/dubbo/internal目录
  loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
  //读取META-INF/dubbo目录
  loadDirectory(extensionClasses, DUBBO_DIRECTORY);
  //读取META-INF/dubbo/services目录
  loadDirectory(extensionClasses, SERVICES_DIRECTORY);
  return extensionClasses;
}
//读取具体的路径
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
  String fileName = dir + type.getName();
  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);
    }
  }
}
//读取具体的文件
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
  BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "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) {
      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) {
        loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
      }
    }
  }
}

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {

  if (clazz.isAnnotationPresent(Adaptive.class)) {
    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());
    }
  } else if (isWrapperClass(clazz)) {
    Set<Class<?>> wrappers = cachedWrapperClasses;
    if (wrappers == null) {
      cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
      wrappers = cachedWrapperClasses;
    }
    wrappers.add(clazz);
  } else {
    clazz.getConstructor();
    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);
      }
    }
    String[] names = NAME_SEPARATOR.split(name);
    if (names != null && names.length > 0) {
      Activate activate = clazz.getAnnotation(Activate.class);
      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);
        if (c == null) {
          extensionClasses.put(n, clazz);
        } else if (c != clazz) {
          throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
        }
      }
    }
  }
}

1.获取普通扩展类

进入ExtensionLoader.getExtension(String name)方法:

public T getExtension(String name) {
  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;
}

大概意思,就是有个缓存,每次获取扩展类时,先读缓存有则返回缓存的实例,没有则新建一个。我们来看看新建一个实例的方法。进入方法ExtensionLoader.createExtension():

private T createExtension(String name) {
  Class<?> clazz = getExtensionClasses().get(name);
  //EXTENSION_INSTANCES是一个Map,key是扩展类Class对象,value是初始化后的实例
  T instance = (T) EXTENSION_INSTANCES.get(clazz);
  if (instance == null) {
    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
    instance = (T) EXTENSION_INSTANCES.get(clazz);
  }
  //@1 注入属性
  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;
}

注入属性

进入ExtensionLoader.injectExtension()方法

private T injectExtension(T instance) {
  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];
				//获取属性名称
        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
        //根据类和属性名称从ExtensionFactory中获取依赖
        Object object = objectFactory.getExtension(pt, property);
        if (object != null) {
          //如果获取到了,则直接设置进去
          method.invoke(instance, object);
        }
      }
    }
  }
  return instance;
}

2.获取active类扩展类

进入方法ExtensionLoader.getActivateExtension():

public List<T> getActivateExtension(URL url, String[] values, String group) {
  List<T> exts = new ArrayList<T>();
  List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
  //如果values里面没有-default,即不是排除掉所有的默认扩展类
  if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
    //加载SPI配置文件
    getExtensionClasses();
    for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
      String name = entry.getKey();
      Activate activate = entry.getValue();
      //根据group查找匹配的类
      if (isMatchGroup(group, activate.group())) {
        T ext = getExtension(name);
        if (!names.contains(name)
            && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
            && isActive(activate, url)) {
          exts.add(ext);
        }
      }
    }
    Collections.sort(exts, ActivateComparator.COMPARATOR);
  }
  List<T> usrs = new ArrayList<T>();
  for (int i = 0; i < names.size(); i++) {
    String name = names.get(i);
    if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
        && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
      if (Constants.DEFAULT_KEY.equals(name)) {
        if (!usrs.isEmpty()) {
          exts.addAll(0, usrs);
          usrs.clear();
        }
      } else {
        T ext = getExtension(name);
        usrs.add(ext);
      }
    }
  }
  if (!usrs.isEmpty()) {
    exts.addAll(usrs);
  }
  return exts;
}

3.获取adaptive类扩展类

public T getAdaptiveExtension() {
  Object instance = cachedAdaptiveInstance.get();
  if (instance == null) {
    synchronized (cachedAdaptiveInstance) {
      instance = cachedAdaptiveInstance.get();
      if (instance == null) {
        instance = createAdaptiveExtension();
        cachedAdaptiveInstance.set(instance);
      }
    }
  }
  return (T) instance;
}

这里的adaptiveExtension是通过动态编译产生的。我们主要看看动态编译的方法:

private Class<?> createAdaptiveExtensionClass() {
	//生成java代码
  String code = createAdaptiveExtensionClassCode();
  ClassLoader classLoader = findClassLoader();
  com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
  //编译成class类
  return compiler.compile(code, classLoader);
}

生成java代码

private String createAdaptiveExtensionClassCode() {
  StringBuilder codeBuilder = new StringBuilder();
  Method[] methods = type.getMethods();
  boolean hasAdaptiveAnnotation = false;
  //判断接口里面是不是有@Adaptive注解
  for (Method m : methods) {
    if (m.isAnnotationPresent(Adaptive.class)) {
      hasAdaptiveAnnotation = true;
      break;
    }
  }
  //没发现@Adaptive注解就报异常
  if (!hasAdaptiveAnnotation)
    throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
	//生成package import 和类名
  codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
  codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
  codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");

  for (Method method : methods) {
    Class<?> rt = method.getReturnType();
    Class<?>[] pts = method.getParameterTypes();
    Class<?>[] ets = method.getExceptionTypes();

    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    if (adaptiveAnnotation == null) {
      code.append("throw new UnsupportedOperationException(\"method ")
        .append(method.toString()).append(" of interface ")
        .append(type.getName()).append(" is not adaptive method!\");");
    } else {
      int urlTypeIndex = -1;
      for (int i = 0; i < pts.length; ++i) {
        if (pts[i].equals(URL.class)) {
          urlTypeIndex = i;
          break;
        }
      }
      // found parameter in URL type
      if (urlTypeIndex != -1) {
        // Null Point check
        String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
                                 urlTypeIndex);
        code.append(s);

        s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
        code.append(s);
      }
      // did not find parameter in URL type
      else {
        String attribMethod = null;

        // find URL getter method
        LBL_PTS:
        for (int i = 0; i < pts.length; ++i) {
          Method[] ms = pts[i].getMethods();
          for (Method m : ms) {
            String name = m.getName();
            if ((name.startsWith("get") || name.length() > 3)
                && Modifier.isPublic(m.getModifiers())
                && !Modifier.isStatic(m.getModifiers())
                && m.getParameterTypes().length == 0
                && m.getReturnType() == URL.class) {
              urlTypeIndex = i;
              attribMethod = name;
              break LBL_PTS;
            }
          }
        }
        if (attribMethod == null) {
          throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
                                          + ": not found url parameter or url attribute in parameters of method " + method.getName());
        }

        // Null point check
        String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
                                 urlTypeIndex, pts[urlTypeIndex].getName());
        code.append(s);
        s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
                          urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
        code.append(s);

        s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
        code.append(s);
      }

      String[] value = adaptiveAnnotation.value();
      // value is not set, use the value generated from class name as the key
      if (value.length == 0) {
        char[] charArray = type.getSimpleName().toCharArray();
        StringBuilder sb = new StringBuilder(128);
        for (int i = 0; i < charArray.length; i++) {
          if (Character.isUpperCase(charArray[i])) {
            if (i != 0) {
              sb.append(".");
            }
            sb.append(Character.toLowerCase(charArray[i]));
          } else {
            sb.append(charArray[i]);
          }
        }
        value = new String[]{sb.toString()};
      }

      boolean hasInvocation = false;
      for (int i = 0; i < pts.length; ++i) {
        if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
          // Null Point check
          String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
          code.append(s);
          s = String.format("\nString methodName = arg%d.getMethodName();", i);
          code.append(s);
          hasInvocation = true;
          break;
        }
      }

      String defaultExtName = cachedDefaultName;
      String getNameCode = null;
      for (int i = value.length - 1; i >= 0; --i) {
        if (i == value.length - 1) {
          if (null != defaultExtName) {
            if (!"protocol".equals(value[i]))
              if (hasInvocation)
                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
            else
              getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
            else
              getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
          } else {
            if (!"protocol".equals(value[i]))
              if (hasInvocation)
                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
            else
              getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
            else
              getNameCode = "url.getProtocol()";
          }
        } else {
          if (!"protocol".equals(value[i]))
            if (hasInvocation)
              getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
          else
            getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
          else
            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
        }
      }
      code.append("\nString extName = ").append(getNameCode).append(";");
      // check extName == null?
      String s = String.format("\nif(extName == null) " +
                               "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
                               type.getName(), Arrays.toString(value));
      code.append(s);

      s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
      code.append(s);

      // return statement
      if (!rt.equals(void.class)) {
        code.append("\nreturn ");
      }

      s = String.format("extension.%s(", method.getName());
      code.append(s);
      for (int i = 0; i < pts.length; i++) {
        if (i != 0)
          code.append(", ");
        code.append("arg").append(i);
      }
      code.append(");");
    }

    codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");
    for (int i = 0; i < pts.length; i++) {
      if (i > 0) {
        codeBuilder.append(", ");
      }
      codeBuilder.append(pts[i].getCanonicalName());
      codeBuilder.append(" ");
      codeBuilder.append("arg").append(i);
    }
    codeBuilder.append(")");
    if (ets.length > 0) {
      codeBuilder.append(" throws ");
      for (int i = 0; i < ets.length; i++) {
        if (i > 0) {
          codeBuilder.append(", ");
        }
        codeBuilder.append(ets[i].getCanonicalName());
      }
    }
    codeBuilder.append(" {");
    codeBuilder.append(code.toString());
    codeBuilder.append("\n}");
  }
  codeBuilder.append("\n}");
  if (logger.isDebugEnabled()) {
    logger.debug(codeBuilder.toString());
  }
  return codeBuilder.toString();
}

结语

dubbo的SPI机制是其可以保持高扩展性的基础

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值