众所周知,Dubbo 是阿里巴巴公司自主研发开源的一个高性能的服务框架(现已捐献给 Apache 基金会组织),应用之间能够经过 RPC 的方式来互相调用并返回结果。主要基于 Java 语言开发,它提供了三大核心能力:java
1. 面向接口的远程方法调用;git
2. 智能容错和负载均衡;github
3. 以及服务自动注册和发现;apache
(图来自 dubbo 官网)缓存
对于 Dubbo 的特性,我这里不作过多的介绍。接下来,进入正题。并发
1. 首先,下载源码,源代码地址为:app
git clone https://github.com/apache/incubator-dubbo.git负载均衡
,若是网速很差的话,耗费时间会稍长一些,请耐心等待。( 本人版本为:2.7.0-SNAPSHOT)框架
2. 下载成功后,用 IDEA 打开,以下图:ide
3. 下面咱们进入正题, 直接看 dubbo-common 模块中的 org.apache.dubbo.common.extension 包,也就是本文重点解读的自定义 ClassLoader 位置。Dubbo 扩展点主要都是从 ExtensionFactory 接口进行的,咱们从这个接口类开始进行分析。
4. ExtensionFactory 接口很简单,里面只有一个方法 getExtension, 而这个 接口一共有三个实现类。分别为:SpiExtensionFactory (SPI 的方式类动态加载)、AdaptiveExtensionFactory(Adaptive 的方式类动态加载)、SpringExtensionFactory(Spring 的方式类动态加载), 这里咱们重点以 SpiExtensionFactory 的方式进行分析。
/** * ExtensionFactory */ @SPI public interface ExtensionFactory { /** * Get extension. * * @param type object type. * @param name object name. * @return object instance. */ <T> T getExtension(Class<T> type, String name); }
5. 进行 SpiExtensionFactory 类中,只有一个 ExtensionFactory 的实现方法。首先,判断该 type 是否为接口类型,而且具备 SPI 注解的标识,则调用 自定义扩张类加载器,对 type 类进行动态加载,若是加载成功,则返回该 type 自适应的类的对象实例。
public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }
6. 接下来,咱们进入 ExtensionLoader.getExtensionLoader(type); 进行分析。对 type 进行类型检查判断,若是不符合要求抛出异常,最后由 EXTENSION_LOADERS.get(type) 获取该类的扩展的 ClassLoader 。
EXTENSION_LOADERS 为 ConcurrentMap, 其中 Key 为 Class<?> 类型,Value 为 ExtensionLoader<?> 类型。也就是说,对应的类有本身的自定义类加载器。
在这里获取的时候为 Null , 接下来 new ExtensionLoader 对象,构造方法中,传入 type ,并回放到 ConcurrentMap 中。
最终,返回 type 对应的自定义 ExtensionLoader 对象。
@SuppressWarnings("unchecked") 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!"); } 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.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
7. 进入到 new ExtensionLoader<T>(type) 中,继续阅读源码。 这里须要注意,该类的构造方法为私有! type 赋值给了全局变量 type 。
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
8. 接下来是 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()) 方法。
这里是连着两个调用, getExtensionLoader 咱们在第 6 步的时候已经阅读过,返回的对象是参数 type 的类加载器。这里返回的是 ExtensionFactory 类加载器,进入到 getAdaptiveExtension() 方法继续阅读。
9. 从 cachedAdaptiveInstance 获取实例, cachedAdaptiveInstance 是一个泛型类,其中只有两个方法 get 、set 两个方法。 还有一个被 volatile 表示的属性 value 。主要是为了将动态加载类对象 instance 临时缓存。
接下来,对 createAdaptiveInstanceError 进行判断。若是不为空,直接抛出 IllegalStateException 信息。
再往下走,对 cachedAdaptiveInstance 进行加锁,再进行一次 非空判断(你应该可以想到了——单例模式),结合上面的 private 构造函数、volatile 和 双重检查判断,能够说种种迹象都是为了不并发形成多个对象建立出现问题。
下面进入真正的建立实例方法 createAdaptiveExtension();
@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; }
10. 在该方法中,首先是获取 class ,最后利用反射成该对象。
@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); } }
11. 调用 getExtensionClasses() ,加载可扩展类。接着判断 cachedAdaptiveClass 是否为空,若是为空,说明该 cachedAdaptive 类还么有初始化,则对其进行初始化。以后,返回该 class 对象。
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
12. 从缓存 Holder 里面获取该类 class ,第一次初始化时为空,对其加锁,同步的方式进行加载;
并对其进行双重检查;执行 loadExtensionClasses() 加载改 class , 并将该 class 类型存入到 cachedClasses 中。这样作有一个好处就是,只须要初始化一次,后期若是须要加载,直接从内存中获取便可。
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; }
13. 因为在在上一步的时候,咱们有看到 synchronized ,因此,这里的 反射加载为 同步的方式。
获取该扩展类上的 SPI 注解信息(关于 SPI 注解,该类比较简单,只有 value 属性,读者能够自行查看),获取其 value 值,以该值做为 cacheName 的 Key。若是 value 值为多个值,以 “,” 分割,取第一个为 cacheName 。
紧接着,分别加载 classpath 下 【META-INF/services/ 】、【META-INF/services/internal/
】和 【META-INF/dubbo/ 】文件夹中的 SPI 配置。
type.name 将 com.alibaba 替换为 org.apache 是为了确保,须要加载的类包老版本兼容问题。统一成 org.apache 版本。
// synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { 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)); } if (names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }
14. 获取 ExtensionLoader 类的 classloader, 若是获取到,使用该 classloader 加载 type 类;未获取到,则使用 java.lang.ClassLoader 从全部的搜索路径中查找 type 名称的资源。正常获取到了,则进行加载 资源。
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { String fileName = dir + type; 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); } }
15. 使用流的方式加载资源(也就是上面提到的 META-INF 目录下的文件),按行读取,并进行加载。这里有一个小细节,须要说明一下,按行读取后,使用了 “=” 分隔符,前面为 name 后面为须要加载的class 类。(为了节省篇幅,这里略去部分代码)
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { ...... BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); ...... while ((line = reader.readLine()) != null) { ...... name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); ...... } } } ...... }
也就是为何咱们可以看到在相似下图文件中所看到的内容(JDK 自带的 SPI 方式,是不能解析 xx=xxx.xxx ),dubbo 经过扩展的方式,进行了支持,同时这个等号左边的 name, 也就是咱们在动态配置时,须要指定的名称。能够说,具备一箭双雕的功效。
16. 到 loadClass 方式中,开始了真正的类加载工做。
这个方法主要是对 clazz 进行加载,并对该动态类上的不一样类型分别作不一样的初始化工做,将加载后的 clazz 对象放入到 cachedNames 、cachedActivates、cachedAdaptiveClass 或 cachedWrapperClasses 中,进行了缓存。方便下一次获取时不用再次加载。
源码,读者可自行查看。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { 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."); } 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()); } } } } }
以上内容为 Dubbo 动态加载扩展类的机制。
================================================
因为本文做者水平有限,不免有些内容分析错误。
感谢你的理解与反馈!