Dubbo SPI

淡淡的烟草味﹌ 2022-05-01 12:54 290阅读 0赞

前言

前面已经讲过JDK SPI, dubbo SPI主要基于JDK SPI机制,进行了增强,实现可扩展

  1. 为什么不直接用JDKSPI呢,主要有哪些改变呢,先列举一下
  2. 1. JDKSPI使用的是for循环通过if判断过去指定的spi对象,而dubbo使用key-value形式获取
  3. 2. JDKSPI不支持默认值,dubbo增加了默认值的设计 比如dubboProtocol默认dubbo
  4. 3. JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源
  5. 4. 如果扩展点加载失败,连扩展点的名称都拿不到了
  6. 5. dubbo增加了对扩展点IoCAOP的支持,一个扩展点可以直接setter注入其它扩展点

代码示例

  1. public class TestExtension {
  2. public static void main(String[] args) {
  3. ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
  4. final Protocol dubboProtocol = loader.getExtension("dubbo");
  5. final Protocol adaptiveExtension = loader.getAdaptiveExtension();
  6. }
  7. }
  8. 复制代码

源码解析

dubbo的SPI 主要是ExtensionLoader这个类可以类比为JDK-SPI中的ServiceLoader

  1. 首先来看一下ExtensionLoader的类属性:
  2. /** 存放SPI文件的三个目录,其中META-INF/services/也是jdk的SPI文件的存放目录 */
  3. private static final String SERVICES_DIRECTORY = "META-INF/services/";
  4. private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
  5. private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";//这个是最终jar包中存放spi文件的位置
  6. private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
  7. /** key: SPI接口Class value: 该接口的ExtensionLoader */
  8. private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS = new ConcurrentHashMap, ExtensionLoader>();
  9. /** key: SPI接口Class value: SPI实现类的对象实例 */
  10. private static final ConcurrentMap, Object> EXTENSION_INSTANCES = new ConcurrentHashMap, Object>();
  11. 再来看一下ExtensionLoader的实例属性:
  12. /** SPI接口Class */
  13. private final Class type;
  14. /** SPI实现类对象实例的创建工厂 */
  15. private final ExtensionFactory objectFactory;
  16. /** key: ExtensionClass的Class value: SPI实现类的key */
  17. private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap, String>();
  18. /** 存放所有的extensionClass */
  19. private final Holder>> cachedClasses = new Holder>>();
  20. private final Map cachedActivates = new ConcurrentHashMap();
  21. /** 缓存创建好的extensionClass实例 */
  22. private final ConcurrentMap> cachedInstances = new ConcurrentHashMap>();
  23. /** 缓存创建好的适配类实例 */
  24. private final Holder cachedAdaptiveInstance = new Holder();
  25. /** 存储类上带有@Adaptive注解的Class */
  26. private volatile Class cachedAdaptiveClass = null;
  27. /** 默认的SPI文件中的key */
  28. private String cachedDefaultName;
  29. /** 存储在创建适配类实例这个过程中发生的错误 */
  30. private volatile Throwable createAdaptiveInstanceError;
  31. /** 存放具有一个type入参的构造器的实现类的Class对象 */
  32. private Set> cachedWrapperClasses;
  33. /** key :实现类的全类名 value: exception, 防止真正的异常被吞掉 */
  34. private Map exceptions = new ConcurrentHashMap();

ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class);

  1. public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
  2. if (type == null)
  3. throw new IllegalArgumentException("Extension type == null");
  4. if (!type.isInterface()) {
  5. throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
  6. }
  7. if (!withExtensionAnnotation(type)) {
  8. throw new IllegalArgumentException("Extension type(" + type +
  9. ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
  10. }
  11. ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  12. if (loader == null) {
  13. EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
  14. loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  15. }
  16. return loader;
  17. }
  18. 复制代码

通过type获取到对应的ExtensionLoader并放入全局缓存中EXTENSION_LOADERS 创建ExtensionLoader

  1. private ExtensionLoader(Class<?> type) {
  2. this.type = type;
  3. objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
  4. }
  5. 复制代码
  6. 当前创建的ExtensionLoader对象的typecom.alibaba.dubbo.rpc.Protocol
  7. 所以此时会执行:ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
  8. 1. 先创建ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
  9. 2. 因为typecom.alibaba.dubbo.common.extension.ExtensionFactory
  10. 所以objectFactorynull。之后,这个ExtensionLoader被放入EXTENSION_LOADERS缓存
  11. 3. ExtensionLoader对象的getAdaptiveExtension()
  12. /**
  13. * 首先从cachedAdaptiveInstance缓存中获取AdaptiveExtension实例
  14. * 如果不为null, 直接返回;
  15. * 如果为null, 先创建AdaptiveExtension实例, 之后放入cachedAdaptiveInstance缓存中,最后返回
  16. */
  17. public T getAdaptiveExtension() {
  18. Object instance = cachedAdaptiveInstance.get();
  19. if (instance == null) {
  20. if (createAdaptiveInstanceError == null) {
  21. synchronized (cachedAdaptiveInstance) {
  22. instance = cachedAdaptiveInstance.get();
  23. if (instance == null) {
  24. try {
  25. instance = createAdaptiveExtension();
  26. cachedAdaptiveInstance.set(instance);
  27. } catch (Throwable t) {
  28. createAdaptiveInstanceError = t;
  29. throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
  30. }
  31. }
  32. }
  33. } else {
  34. throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
  35. }
  36. }
  37. return (T) instance;
  38. }
  39. 复制代码
  40. 创建AdaptiveExtension
  41. 终返回对应的实例对象
  42. /**
  43. * createAdaptiveExtension()
  44. * --getAdaptiveExtensionClass()
  45. * //从dubbo-spi配置文件中获取AdaptiveExtensionClass
  46. * --getExtensionClasses()
  47. * --loadExtensionClasses()
  48. * --loadFile(Map<String, Class<?>> extensionClasses, String dir)
  49. * //创建动态代理类
  50. * --createAdaptiveExtensionClass()
  51. *
  52. * --injectExtension(T instance) //dubbo-ioc
  53. */
  54. private T createAdaptiveExtension() {
  55. try {
  56. return injectExtension((T) getAdaptiveExtensionClass().newInstance());
  57. } catch (Exception e) {
  58. throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
  59. }
  60. }
  61. 复制代码
  62. 调用层级看注释。
  63. injectExtension(T instance)方法只对objectFactory有用,
  64. 如果objectFactory==null,则直接返回T instance
  65. 所以这里返回的是getAdaptiveExtensionClass().newInstance()
  66. /**
  67. * 获取ExtensionClasses和适配类
  68. * 如果实现类上带有@Adaptive注解,直接创建修饰类
  69. * 如果方法上带有@Adaptive注解,动态生成代理类
  70. */
  71. private Class<?> getAdaptiveExtensionClass() {
  72. getExtensionClasses();
  73. if (cachedAdaptiveClass != null) {
  74. return cachedAdaptiveClass;
  75. }
  76. return cachedAdaptiveClass = createAdaptiveExtensionClass();
  77. }
  78. 复制代码
  79. /**
  80. * 先从cachedClasses缓存中获取所有的ExtensionClass,如果有,直接返回;
  81. * 如果没有,通过loadExtensionClasses()从SPI文件中去读取,之后写入缓存
  82. */
  83. private Map<String, Class<?>> getExtensionClasses() {
  84. Map<String, Class<?>> classes = cachedClasses.get();
  85. if (classes == null) {
  86. synchronized (cachedClasses) {
  87. classes = cachedClasses.get();
  88. if (classes == null) {
  89. classes = loadExtensionClasses();
  90. cachedClasses.set(classes);
  91. }
  92. }
  93. }
  94. return classes;
  95. }
  96. 复制代码
  97. /**
  98. * 1 从@SPI注解中将默认值解析出来,并缓存到cachedDefaultName中
  99. * 2 从SPI文件中获取extensionClass并存储到extensionClasses中,最后返回extensionClasses
  100. * 注意:此方法已经getExtensionClasses方法同步过。
  101. */
  102. private Map<String, Class<?>> loadExtensionClasses() {
  103. final SPI defaultAnnotation = type.getAnnotation(SPI.class);
  104. if (defaultAnnotation != null) {
  105. String value = defaultAnnotation.value();
  106. if (value != null && (value = value.trim()).length() > 0) {
  107. String[] names = NAME_SEPARATOR.split(value);
  108. if (names.length > 1) {
  109. throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
  110. + ": " + Arrays.toString(names));
  111. }
  112. if (names.length == 1) cachedDefaultName = names[0];
  113. }
  114. }
  115. Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
  116. loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
  117. loadFile(extensionClasses, DUBBO_DIRECTORY);
  118. loadFile(extensionClasses, SERVICES_DIRECTORY);
  119. return extensionClasses;
  120. }
  121. 复制代码

之后来看一下非常重要的一个方法loadFile(Map> extensionClasses, String dir)

  1. /**
  2. * 1 加载dir目录下的指定type名称的文件(例如:dubbo-2.5.5.jar中的/META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory)
  3. * 2 遍历该文件中的每一行
  4. * (1)获取实现类key和value, 例如 name=spi, line=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
  5. * (2)根据line创建Class对象
  6. * (3)将具有@Adaptive注解的实现类的Class对象放在cachedAdaptiveClass缓存中, 注意该缓存只能存放一个具有@Adaptive注解的实现类的Class对象,如果有两个满足条件,则抛异常
  7. * 下面的都是对不含@Adaptive注解的实现类的Class对象:
  8. * (4)查看是否具有含有一个type入参的构造器, 如果有(就是wrapper类), 将当前的Class对象放置到cachedWrapperClasses缓存中
  9. * (5)如果没有含有一个type入参的构造器, 获取无参构造器. 如果Class对象具有@Active注解, 将该对象以<实现类的key, active>存储起来
  10. * (6)最后,将<Class对象, 实现类的key>存入cachedNames缓存,并将这些Class存入extensionClasses中.
  11. * @param extensionClasses
  12. * @param dir
  13. */
  14. private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
  15. String fileName = dir + type.getName();
  16. try {
  17. Enumeration<java.net.URL> urls;
  18. ClassLoader classLoader = findClassLoader();
  19. if (classLoader != null) {
  20. urls = classLoader.getResources(fileName);
  21. } else {
  22. urls = ClassLoader.getSystemResources(fileName);
  23. }
  24. if (urls != null) {
  25. while (urls.hasMoreElements()) {
  26. java.net.URL url = urls.nextElement();
  27. try {
  28. BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
  29. try {
  30. String line = null;
  31. while ((line = reader.readLine()) != null) {
  32. final int ci = line.indexOf('#');
  33. if (ci >= 0) line = line.substring(0, ci);
  34. line = line.trim();
  35. if (line.length() > 0) {
  36. try {
  37. String name = null;
  38. int i = line.indexOf('=');
  39. if (i > 0) {
  40. name = line.substring(0, i).trim();
  41. line = line.substring(i + 1).trim();
  42. }
  43. if (line.length() > 0) {
  44. Class<?> clazz = Class.forName(line, true, classLoader);
  45. if (!type.isAssignableFrom(clazz)) {
  46. throw new IllegalStateException("Error when load extension class(interface: " +
  47. type + ", class line: " + clazz.getName() + "), class "
  48. + clazz.getName() + "is not subtype of interface.");
  49. }
  50. if (clazz.isAnnotationPresent(Adaptive.class)) {
  51. if (cachedAdaptiveClass == null) {
  52. cachedAdaptiveClass = clazz;
  53. } else if (!cachedAdaptiveClass.equals(clazz)) {
  54. throw new IllegalStateException("More than 1 adaptive class found: "
  55. + cachedAdaptiveClass.getClass().getName()
  56. + ", " + clazz.getClass().getName());
  57. }
  58. } else {
  59. try {
  60. clazz.getConstructor(type);
  61. Set<Class<?>> wrappers = cachedWrapperClasses;
  62. if (wrappers == null) {
  63. cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
  64. wrappers = cachedWrapperClasses;
  65. }
  66. wrappers.add(clazz);
  67. } catch (NoSuchMethodException e) {
  68. clazz.getConstructor();
  69. if (name == null || name.length() == 0) {
  70. name = findAnnotationName(clazz);
  71. if (name == null || name.length() == 0) {
  72. if (clazz.getSimpleName().length() > type.getSimpleName().length()
  73. && clazz.getSimpleName().endsWith(type.getSimpleName())) {
  74. name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
  75. } else {
  76. throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
  77. }
  78. }
  79. }
  80. String[] names = NAME_SEPARATOR.split(name);
  81. if (names != null && names.length > 0) {
  82. Activate activate = clazz.getAnnotation(Activate.class);
  83. if (activate != null) {
  84. cachedActivates.put(names[0], activate);
  85. }
  86. for (String n : names) {
  87. if (!cachedNames.containsKey(clazz)) {
  88. cachedNames.put(clazz, n);
  89. }
  90. Class<?> c = extensionClasses.get(n);
  91. if (c == null) {
  92. extensionClasses.put(n, clazz);
  93. } else if (c != clazz) {
  94. throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
  95. }
  96. }
  97. }
  98. }
  99. }
  100. }
  101. } catch (Throwable t) {
  102. IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
  103. exceptions.put(line, e);
  104. }
  105. }
  106. } // end of while read lines
  107. } finally {
  108. reader.close();
  109. }
  110. } catch (Throwable t) {
  111. logger.error("Exception when load extension class(interface: " +
  112. type + ", class file: " + url + ") in " + url, t);
  113. }
  114. } // end of while urls
  115. }
  116. } catch (Throwable t) {
  117. logger.error("Exception when load extension class(interface: " +
  118. type + ", description file: " + fileName + ").", t);
  119. }
  120. }
  121. 复制代码
  122. 上述的方法分别从三个目录查找SPI文件并进行加载。在这里只有在META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory中有值,内容如下:
  123. 1 adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
  124. 2 spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
  125. 3 spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

执行之后:

  1. getAdaptiveExtensionClass()返回的com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
  2. 之后来看一下其newInstance()代码,调用这个方法,默认会执行AdaptiveExtensionFactory的无参构造器。这里给出AdaptiveExtensionFactory的完整代码
  3. @Adaptive
  4. public class AdaptiveExtensionFactory implements ExtensionFactory {
  5. private final List<ExtensionFactory> factories;
  6. public AdaptiveExtensionFactory() {
  7. ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
  8. List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
  9. for (String name : loader.getSupportedExtensions()) {
  10. list.add(loader.getExtension(name));
  11. }
  12. factories = Collections.unmodifiableList(list);
  13. }
  14. @Override
  15. public <T> T getExtension(Class<T> type, String name) {
  16. for (ExtensionFactory factory : factories) {
  17. T extension = factory.getExtension(type, name);
  18. if (extension != null) {
  19. return extension;
  20. }
  21. }
  22. return null;
  23. }
  24. }
  25. 复制代码

从上可以看出,这个装饰类只是实例化好了各个ExtensionFactory(这里是SpiExtensionFactory和SpringExtensionFactory),后续通过工厂获取实现类实例都是由具体工厂来完成。

  1. 小结:
  2. ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Class type)最终得到的实例变量是:
  3. 1. Class type = interface T
  4. 2. ExtensionFactory objectFactory = AdaptiveExtensionFactory(适配类)
  5. factories = [SpringExtensionFactory实例, SpiExtensionFactory实例]

Protocol dubboProtocol = loader.getExtension(“dubbo”)

  1. /**
  2. * 从cachedInstances缓存中获取name对应的实例,如果没有,通过createExtension(name)创建,之后放入缓存
  3. * getExtension(String name)
  4. * --createExtension(String name)
  5. * ----injectExtension(T instance)
  6. */
  7. public T getExtension(String name) {
  8. if (name == null || name.length() == 0)
  9. throw new IllegalArgumentException("Extension name == null");
  10. if ("true".equals(name)) {
  11. return getDefaultExtension();
  12. }
  13. Holder<Object> holder = cachedInstances.get(name);
  14. if (holder == null) {
  15. cachedInstances.putIfAbsent(name, new Holder<Object>());
  16. holder = cachedInstances.get(name);
  17. }
  18. Object instance = holder.get();
  19. if (instance == null) {
  20. synchronized (holder) {
  21. instance = holder.get();
  22. if (instance == null) {
  23. instance = createExtension(name);
  24. holder.set(instance);
  25. }
  26. }
  27. }
  28. return (T) instance;
  29. }
  30. 复制代码
  31. private T createExtension(String name) {
  32. /** 从cachedClasses缓存中获取所有的实现类map,之后通过name获取到对应的实现类的Class对象 */
  33. Class<?> clazz = getExtensionClasses().get(name);
  34. if (clazz == null) {
  35. throw findException(name);
  36. }
  37. try {
  38. /** 从EXTENSION_INSTANCES缓存中获取对应的实现类的Class对象,如果没有,直接创建,之后放入缓存 */
  39. T instance = (T) EXTENSION_INSTANCES.get(clazz);
  40. if (instance == null) {
  41. EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
  42. instance = (T) EXTENSION_INSTANCES.get(clazz);
  43. }
  44. injectExtension(instance);//ioc
  45. Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  46. if (wrapperClasses != null && wrapperClasses.size() > 0) {
  47. for (Class<?> wrapperClass : wrapperClasses) {
  48. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
  49. }
  50. }
  51. return instance;
  52. } catch (Throwable t) {
  53. throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
  54. type + ") could not be instantiated: " + t.getMessage(), t);
  55. }
  56. }
  57. 复制代码

转载于:https://juejin.im/post/5c91fd756fb9a070d90f5cca

发表评论

表情:
评论列表 (有 0 条评论,290人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Dubbo SPI

    前言 前面已经讲过[JDK SPI][], dubbo SPI主要基于JDK SPI机制,进行了增强,实现可扩展 为什么不直接用JDK的SPI呢,主要有哪些改变