dubbo-spi源码解析(2)

未来已来2018-11-25 11:02

此文已由作者赵计刚薪授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


之后来看一下非常重要的一个方法loadFile(Map<String, Class<?>> 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     }

上述的方法分别从三个目录查找SPI文件并进行加载。在这里只有在META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory中有值,内容如下:

1 adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
2 spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
3 spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

其中AdaptiveExtensionFactory在类上具有@Adaptive注解,这个类会在后续去讲,这里先略过。

执行过后,看一下:

  • cachedAdaptiveClass=class com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
  • extensionClasses=[{"spring","class com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory"}, {"spi", "class com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory"}],后续会这个集合存储在cachedClasses缓存中。

上边一直在讲解getAdaptiveExtensionClass().newInstance()这句代码中的getAdaptiveExtensionClass(),此方法返回一个com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory类,之后来看一下其newInstance()代码,调用这个方法,默认会执行AdaptiveExtensionFactory的无参构造器。这里给出AdaptiveExtensionFactory的完整代码:

 1 package com.alibaba.dubbo.common.extension.factory;
 2 
 3 import com.alibaba.dubbo.common.extension.Adaptive;
 4 import com.alibaba.dubbo.common.extension.ExtensionFactory;
 5 import com.alibaba.dubbo.common.extension.ExtensionLoader;
 6 
 7 import java.util.ArrayList;
 8 import java.util.Collections;
 9 import java.util.List;
10 
11 /**
12  * AdaptiveExtensionFactory
13  */
14 @Adaptive
15 public class AdaptiveExtensionFactory implements ExtensionFactory {
16     private final List<ExtensionFactory> factories;
17 
18     /**
19      * 遍历cachedClasses中缓存的extensionClasses的key,之后根据key来实例化对应的实现类,最后放置到EXTENSION_INSTANCES缓存中
20      */
21     public AdaptiveExtensionFactory() {
22         ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
23         List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
24         for (String name : loader.getSupportedExtensions()) {
25             list.add(loader.getExtension(name));
26         }
27         factories = Collections.unmodifiableList(list);
28     }
29 
30     public <T> T getExtension(Class<T> type, String name) {
31         for (ExtensionFactory factory : factories) {
32             T extension = factory.getExtension(type, name);
33             if (extension != null) {
34                 return extension;
35             }
36         }
37         return null;
38     }
39 }

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

来看一下实例化代码的地方,即loader.getExtension(name):

 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     }

来看一下创建createExtension(name):

 1     private T createExtension(String name) {
 2         /** 从cachedClasses缓存中获取所有的实现类map,之后通过name获取到对应的实现类的Class对象 */
 3         Class<?> clazz = getExtensionClasses().get(name);
 4         if (clazz == null) {
 5             throw findException(name);
 6         }
 7         try {
 8             /** 从EXTENSION_INSTANCES缓存中获取对应的实现类的Class对象,如果没有,直接创建,之后放入缓存 */
 9             T instance = (T) EXTENSION_INSTANCES.get(clazz);
10             if (instance == null) {
11                 EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
12                 instance = (T) EXTENSION_INSTANCES.get(clazz);
13             }
14             injectExtension(instance);//ioc
15             Set<Class<?>> wrapperClasses = cachedWrapperClasses;
16             if (wrapperClasses != null && wrapperClasses.size() > 0) {
17                 for (Class<?> wrapperClass : wrapperClasses) {
18                     instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
19                 }
20             }
21             return instance;
22         } catch (Throwable t) {
23             throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
24                     type + ")  could not be instantiated: " + t.getMessage(), t);
25         }
26     }

这里,就体现出来了dubbo-SPI比JDK-SPI的好处:dubbo-SPI不需要遍历所有的实现类来获取想要的实现类,可以直接通过name来获取。

injectExtension(instance)和wrapper包装功能后续再说。

 

到此为止,ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);这行代码的整个源码就讲述完成了。最后来看一下整个代码的执行结果。

类变量

  • ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS
    • "interface com.alibaba.dubbo.rpc.Protocol" -> "com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.rpc.Protocol]"
    • "interface com.alibaba.dubbo.common.extension.ExtensionFactory" -> "com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.common.extension.ExtensionFactory]"
  • ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES
    • "class com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory" -> SpringExtensionFactory实例
    • "class com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory" -> SpiExtensionFactory实例

ExtensionLoader<Protocol> loader的实例变量

  • Class<?> type = interface com.alibaba.dubbo.rpc.Protocol
  • ExtensionFactory objectFactory = AdaptiveExtensionFactory(适配类)
    • factories = [SpringExtensionFactory实例, SpiExtensionFactory实例]

总结:

第一点:ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(Class<T> type)最终得到的实例变量是:

  • Class<?> type = interface T
  • ExtensionFactory objectFactory = AdaptiveExtensionFactory(适配类)
    • factories = [SpringExtensionFactory实例, SpiExtensionFactory实例]

第二点:ExtensionLoader<T>.getAdaptiveExtension()的调用层级

1 ExtensionLoader<T>.getAdaptiveExtension()
2 --createAdaptiveExtension()
3 ----injectExtension(getAdaptiveExtensionClass())
4 ------getAdaptiveExtensionClass()
5 --------getExtensionClasses()//从spi文件中查找实现类上具有@Adaptive注解的类
6 ----------loadExtensionClasses()
7 ------------loadFile(Map<String, Class<?>> extensionClasses, String dir)
8 --------createAdaptiveExtensionClass()//如果从spi文件中没有找到实现类上具有@Adaptive注解的类,则动态创建类

最终返回的是创建好的Adaptive类,例如AdaptiveExtensionFactory实例。

第三点:ExtensionLoader<T>.getExtension()的调用层级

1 ExtensionLoader<T>.getExtension()
2 --createExtension(String name)
3 ----getExtensionClasses().get(name)//获取扩展类
4 ----injectExtension(instance);//ioc
5 ----wrapper包装;//aop

最终返回的是创建好的具体实现类,例如SpringExtensionFactory实例。 

 

至此,dubbo-spi源码解析完成!!!


免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击