Dubbo源码分析-SPI机制

亦凉 2022-01-13 07:13 530阅读 0赞

本源码基于dubbo2.6.6

参考dubbo官网:dubbo官网SPI解读

dubbo的SPI使用

  1. //加载SPI标签的类放到缓存中
  2. ExtensionLoader<MySPIDemo> extensionLoader = ExtensionLoader.getExtensionLoader(MySPIDemo.class);
  3. //获取SPI类
  4. MySPIDemo mySPIDemo = extensionLoader.getExtension("method");

加载SPI的入口getExtensionLoader
如果存在缓存中从缓存取,如果不存在那么就new ExtensionLoader(type)放入缓存并且返回

  1. @SuppressWarnings("unchecked")
  2. public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
  3. //校验类
  4. if (type == null)
  5. throw new IllegalArgumentException("Extension type == null");
  6. //校验是否为接口,不是接口会报错
  7. if (!type.isInterface()) {
  8. throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
  9. }
  10. //校验是否带有SPI注解
  11. if (!withExtensionAnnotation(type)) {
  12. throw new IllegalArgumentException("Extension type(" + type +
  13. ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
  14. }
  15. //缓存中获取
  16. ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  17. if (loader == null) {
  18. //加入缓存
  19. EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
  20. loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  21. }
  22. return loader;
  23. }

这个构造方法如果type不是ExtensionFactory那么objectFactory就是ExtensionFactory创建的自适应拓展类(略有点绕)

  1. private ExtensionLoader(Class<?> type) {
  2. this.type = type;
  3. objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
  4. }

从缓存(Holder)cachedAdaptiveInstance获取自适应拓展的实例
没有就通过createAdaptiveExtension()创建实例

这里T是ExtensionFactory

记住2点:

  1. 取缓存。
  2. 没缓存,创建

    @SuppressWarnings(“unchecked”)
    public T getAdaptiveExtension() {

    1. //先从缓存Holder中取
    2. Object instance = cachedAdaptiveInstance.get();
    3. if (instance == null) {
    4. if (createAdaptiveInstanceError == null) {
    5. //加锁判断
    6. synchronized (cachedAdaptiveInstance) {
    7. instance = cachedAdaptiveInstance.get();
    8. if (instance == null) {
    9. try {
    10. //创建自适应拓展类,放入缓存
    11. instance = createAdaptiveExtension();
    12. cachedAdaptiveInstance.set(instance);
    13. } catch (Throwable t) {
    14. createAdaptiveInstanceError = t;
    15. throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
    16. }
    17. }
    18. }
    19. } else {
    20. throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
    21. }
    22. }
    23. return (T) instance;

    }

createAdaptiveExtension通过反射创建实例
1.getAdaptiveExtensionClass找寻自适应拓展类的class对象
2.反射创建实例
3.injectExtension???

  1. @SuppressWarnings("unchecked")
  2. private T createAdaptiveExtension() {
  3. try {
  4. return injectExtension((T) getAdaptiveExtensionClass().newInstance());
  5. } catch (Exception e) {
  6. throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
  7. }
  8. }

获取自适应拓展类的字节码对象

  1. private Class<?> getAdaptiveExtensionClass() {
  2. getExtensionClasses();
  3. if (cachedAdaptiveClass != null) {
  4. return cachedAdaptiveClass;
  5. }
  6. return cachedAdaptiveClass = createAdaptiveExtensionClass();
  7. }

获取所有的自适应拓展类
先从缓存取,缓存没有就加载

  1. private Map<String, Class<?>> getExtensionClasses() {
  2. //从holder里面取缓存
  3. Map<String, Class<?>> classes = cachedClasses.get();
  4. if (classes == null) {
  5. synchronized (cachedClasses) {
  6. classes = cachedClasses.get();
  7. if (classes == null) {
  8. //加载自适应拓展类字节码
  9. classes = loadExtensionClasses();
  10. //放入缓存
  11. cachedClasses.set(classes);
  12. }
  13. }
  14. }
  15. return classes;
  16. }

从目录中获取

private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + “internal/”;
private static final String DUBBO_DIRECTORY = “META-INF/dubbo/”;
private static final String SERVICES_DIRECTORY = “META-INF/services/”;

  1. private Map<String, Class<?>> loadExtensionClasses() {
  2. //判断类是不是有SPI标签(ExtensionFactory)
  3. //ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
  4. //type=ExtensionFactory 这个类是有SPI标签的
  5. final SPI defaultAnnotation = type.getAnnotation(SPI.class);
  6. if (defaultAnnotation != null) {
  7. //获取spi的属性值
  8. String value = defaultAnnotation.value();
  9. if ((value = value.trim()).length() > 0) {
  10. //根据正则表达式切分("\\s*[,]+\\s*")
  11. String[] names = NAME_SEPARATOR.split(value);
  12. //只允许有一个
  13. if (names.length > 1) {
  14. throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
  15. + ": " + Arrays.toString(names));
  16. }
  17. //获取缓存的名称
  18. if (names.length == 1) cachedDefaultName = names[0];
  19. }
  20. }
  21. //加载放入Map中,只有普通类才会加入到map中,自适应类不会加载进去
  22. Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
  23. loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
  24. loadDirectory(extensionClasses, DUBBO_DIRECTORY);
  25. loadDirectory(extensionClasses, SERVICES_DIRECTORY);
  26. return extensionClasses;
  27. }

加载指定路径下的文件名是type的

  1. private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
  2. //获取路径下的指定名称的文件名,见下方的图
  3. String fileName = dir + type.getName();
  4. try {
  5. Enumeration<java.net.URL> urls;
  6. ClassLoader classLoader = findClassLoader();
  7. if (classLoader != null) {
  8. urls = classLoader.getResources(fileName);
  9. } else {
  10. urls = ClassLoader.getSystemResources(fileName);
  11. }
  12. if (urls != null) {
  13. while (urls.hasMoreElements()) {
  14. java.net.URL resourceURL = urls.nextElement();
  15. loadResource(extensionClasses, classLoader, resourceURL);
  16. }
  17. }
  18. } catch (Throwable t) {
  19. logger.error("Exception when load extension class(interface: " +
  20. type + ", description file: " + fileName + ").", t);
  21. }
  22. }

我们看一下这个文件的内容
找到META-INF下对应的文件除去注释并且加载class

  1. private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
  2. try {
  3. BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
  4. try {
  5. String line;
  6. while ((line = reader.readLine()) != null) {
  7. final int ci = line.indexOf('#');
  8. if (ci >= 0) line = line.substring(0, ci);
  9. line = line.trim();
  10. if (line.length() > 0) {
  11. try {
  12. String name = null;
  13. int i = line.indexOf('=');
  14. if (i > 0) {
  15. name = line.substring(0, i).trim();
  16. line = line.substring(i + 1).trim();
  17. }
  18. if (line.length() > 0) {
  19. loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
  20. }
  21. } catch (Throwable t) {
  22. IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
  23. exceptions.put(line, e);
  24. }
  25. }
  26. }
  27. } finally {
  28. reader.close();
  29. }
  30. } catch (Throwable t) {
  31. logger.error("Exception when load extension class(interface: " +
  32. type + ", class file: " + resourceURL + ") in " + resourceURL, t);
  33. }
  34. }
  35. private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
  36. if (!type.isAssignableFrom(clazz)) {
  37. throw new IllegalStateException("Error when load extension class(interface: " +
  38. type + ", class line: " + clazz.getName() + "), class "
  39. + clazz.getName() + "is not subtype of interface.");
  40. }
  41. //是否有适应注解
  42. if (clazz.isAnnotationPresent(Adaptive.class)) {
  43. if (cachedAdaptiveClass == null) {
  44. //缓存自适应类
  45. cachedAdaptiveClass = clazz;
  46. } else if (!cachedAdaptiveClass.equals(clazz)) {
  47. throw new IllegalStateException("More than 1 adaptive class found: "
  48. + cachedAdaptiveClass.getClass().getName()
  49. + ", " + clazz.getClass().getName());
  50. }
  51. //是否是wrapper类型(clazz是否有一个参数是type的构造方法)
  52. //clazz=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
  53. //type=com.alibaba.dubbo.common.extension.ExtensionFactory
  54. } else if (isWrapperClass(clazz)) {
  55. //将clazz放入缓存中
  56. Set<Class<?>> wrappers = cachedWrapperClasses;
  57. if (wrappers == null) {
  58. cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
  59. wrappers = cachedWrapperClasses;
  60. }
  61. wrappers.add(clazz);
  62. } else {
  63. //既不是warpper类也没有Adaptive注解
  64. clazz.getConstructor();
  65. //name=spring
  66. if (name == null || name.length() == 0) {
  67. //获取extension注解的value,如果没有就用小写的类名
  68. name = findAnnotationName(clazz);
  69. if (name.length() == 0) {
  70. throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
  71. }
  72. }
  73. String[] names = NAME_SEPARATOR.split(name);
  74. if (names != null && names.length > 0) {
  75. //如果不为空,那么获取activate注解
  76. Activate activate = clazz.getAnnotation(Activate.class);
  77. //如果有注解就放入缓存中
  78. if (activate != null) {
  79. cachedActivates.put(names[0], activate);
  80. }
  81. for (String n : names) {
  82. //如果缓存的名字不存在那么放入缓存中
  83. if (!cachedNames.containsKey(clazz)) {
  84. cachedNames.put(clazz, n);
  85. }
  86. //再去缓存extensionClasses中找寻对应的字节码对象
  87. Class<?> c = extensionClasses.get(n);
  88. //如果不存在就把clazz放进缓存
  89. if (c == null) {
  90. extensionClasses.put(n, clazz);
  91. } else if (c != clazz) {
  92. throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
  93. }
  94. }
  95. }
  96. }
  97. }

获取extension注解的value,如果没有就用小写的类名

  1. @SuppressWarnings("deprecation")
  2. private String findAnnotationName(Class<?> clazz) {
  3. com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
  4. //获取extension注解的value,如果没有就用小写的类名
  5. if (extension == null) {
  6. String name = clazz.getSimpleName();
  7. if (name.endsWith(type.getSimpleName())) {
  8. name = name.substring(0, name.length() - type.getSimpleName().length());
  9. }
  10. return name.toLowerCase();
  11. }
  12. return extension.value();
  13. }

getExtensionClasses(加载文件中的类)
获取自适应拓展类
injectExtension((T) getAdaptiveExtensionClass().newInstance());

最后ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取的自适应拓展类是AdaptiveExtensionFactory

ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(MySPIDemo.class);

ExtensionLoader
private final Class<?> type=MySPIDemo.class;
private final ExtensionFactory objectFactory=AdaptiveExtensionFactory;

分析完成

getExtensionLoader总结:

  1. 获取ExtensionLoader会先从EXTENSION_LOADERS缓存中取。
  2. 如果是ExtensionFactory类objectFactory对象创建工厂就是空,否则是ExtensionFactory的自适应拓展类
  3. 获取自适应拓展类,先查询缓存cachedAdaptiveInstance,获取自适应的实例,没有就创建拓展类
  4. 先获取字节码对象,再通过反射创建实例
  5. 先获取所有的拓展字节码对象,先从缓存cachedClasses取如果不存在loadExtensionClasses,并且加入缓存
  6. 获取type的SPI注解的值作为缓存cachedDefaultName,然后从不同的目录加载(DUBBO_INTERNAL_DIRECTORY、DUBBO_DIRECTORY、SERVICES_DIRECTORY)
    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + “internal/”;
    private static final String DUBBO_DIRECTORY = “META-INF/dubbo/”;
    private static final String SERVICES_DIRECTORY = “META-INF/services/”;
  7. 通过文件路径去加载资源loadResource并且放入缓存extensionClasses
  8. loadClass,如果类上有Adaptive注解,放入缓存(Class类自适应拓展字节码类)cachedAdaptiveClass
  9. 如果类是wrapper类,构造函数有一个type类型的入参。将字节码对象放入cachedWrapperClasses缓存
  10. 如果类是一个普通类 ,判断有没有Activate注解,如果有的话,name不为空就把name作为key,如果name为空把extension注解的value,如果没有就用小写的类名作为key(name),Activate作为值放入缓存cachedActivates中。
  11. 如果没有Activate注解将字节码对象作为key,name作为value放入缓存cachedNames,查询缓存extensionClasses中是否有name对应的字节码对象,如果不存在就放进去。

缓存类

  1. EXTENSION_LOADERS(ConcurrentMap, ExtensionLoader<?>>)缓存,拓展加载类缓存
  2. cachedAdaptiveInstance(Holder)自适应拓展类实例缓存
  3. cachedClasses(Holder>>所有拓展类的字节码缓存,加载文件的拓展类存放的地方)
  4. cachedAdaptiveClass(Class<?>)缓存拓展类的字节码对象
  5. cachedWrapperClasses(ConcurrentHashSet>)缓存wrapper对象
  6. cachedActivates(Map)缓存Activate
  7. cachedNames(ConcurrentMap, String>)缓存字节码对象和名称

接下来分析
MySPIDemo mySPIDemo = extensionLoader.getExtension(“method”);

  1. @SuppressWarnings("unchecked")
  2. public T getExtension(String name) {
  3. if (name == null || name.length() == 0)
  4. throw new IllegalArgumentException("Extension name == null");
  5. if ("true".equals(name)) {
  6. return getDefaultExtension();
  7. }
  8. //从缓存cachedInstances(ConcurrentMap<String, Holder<Object>>)中获取该名称的Holder
  9. Holder<Object> holder = cachedInstances.get(name);
  10. if (holder == null) {
  11. //将对象放入缓存,取出来放入holder
  12. cachedInstances.putIfAbsent(name, new Holder<Object>());
  13. holder = cachedInstances.get(name);
  14. }
  15. //缓存的实例
  16. Object instance = holder.get();
  17. if (instance == null) {
  18. synchronized (holder) {
  19. instance = holder.get();
  20. if (instance == null) {
  21. //创建拓展类
  22. instance = createExtension(name);
  23. holder.set(instance);
  24. }
  25. }
  26. }
  27. return (T) instance;
  28. }

Dubbo IOC

Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。整个过程对应的代码如下:

  1. private T injectExtension(T instance) {
  2. try {
  3. if (objectFactory != null) {
  4. for (Method method : instance.getClass().getMethods()) {
  5. if (method.getName().startsWith("set")
  6. && method.getParameterTypes().length == 1
  7. && Modifier.isPublic(method.getModifiers())) {
  8. //这个方法可不可以进行反射
  9. if (method.getAnnotation(DisableInject.class) != null) {
  10. continue;
  11. }
  12. //获取参数类型
  13. Class<?> pt = method.getParameterTypes()[0];
  14. try {
  15. //获取set后的参数名称:比如setAge,那么property=age;
  16. String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
  17. Object object = objectFactory.getExtension(pt, property);
  18. if (object != null) {
  19. method.invoke(instance, object);
  20. }
  21. } catch (Exception e) {
  22. logger.error("fail to inject via method " + method.getName()
  23. + " of interface " + type.getName() + ": " + e.getMessage(), e);
  24. }
  25. }
  26. }
  27. }
  28. } catch (Exception e) {
  29. logger.error(e.getMessage(), e);
  30. }
  31. return instance;
  32. }

objectFactory.getExtension有3种实现方式
ExtensionFactory的实现类
通过SPI获取

  1. public class SpiExtensionFactory implements ExtensionFactory {
  2. @Override
  3. public <T> T getExtension(Class<T> type, String name) {
  4. if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
  5. ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
  6. if (!loader.getSupportedExtensions().isEmpty()) {
  7. return loader.getAdaptiveExtension();
  8. }
  9. }
  10. return null;
  11. }
  12. }

发表评论

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

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

相关阅读

    相关 dubbo解析-SPI机制

     架构体系   框架介绍   概述   Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spri

    相关 dubbo 自适应SPI机制分析

    在之前博客中,我们介绍了JAVA SPI 以及 Dubbo  SPI 的基本使用以及源码分析,通过指定扩展点类型,可以创建扩展点的实现类,但是在dubbo中,有些时候并不期望直