Dubbo 源码解析系列一 ExtensionLoader
来源:互联网 发布:淘宝二手收购 编辑:程序博客网 时间:2024/05/17 04:53
Dubbo作为阿里巴巴开源的RPC框架,也是业内被各大公司使用的最多的RPC框架,有些直接使用了Dubbo,有些对Dubbo进行改造,例如当当网使用的Dubbox。从本文开始,笔者将陆续介绍对Dubbo源码进行解析,本节从Dubbo中最为重要的ExtensionLoader开始讲起。
ExtensionLoader存在于common模块下,在Dubbo源码中,经常会看到这一的代码ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getDefaultExtension();下面代码是getExtensionLoader具体实现:
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;}
从以上实现可以看出,EXTENSION_LOADERS是作为ExtensionLoader类型的缓存中心,而EXTENSION_LOADERS的定义是
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
是static的,可以作为类属性使用,公用一个。
在得到一个ExtensionLoader后,我们最主要的是为为了获取类的实例,我们会紧接着看到getDefaultExtension()的实现
public T getDefaultExtension() { getExtensionClasses(); if(null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName);}
先看下getExtensionClasses的实现
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;}
这里使用了双重验证锁,验证classes = cachedClasses.get();是否为null,避免由于多线程造成重复赋值。这里会跟着到loadExtensionClasses方法中去取得具体的classes
private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { String value = defaultAnnotation.value(); if(value != null && (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<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses;}
这里就是具体的加载classes的地方了,这段代码最终会根据loadFile方法,讲三个文件夹下的文件都加载当作扩展类,那么具体看最重要的一段代码loadFile是如何加载类的
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { String fileName = dir + type.getName(); 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 url = urls.nextElement(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); try { String line = null; 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) { try { 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) { Class<?> clazz = Class.forName(line, true, classLoader); 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 { try { clazz.getConstructor(type); Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name == null || name.length() == 0) { if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); } else { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); } } } 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()); } } } } } } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } // end of while read lines } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + url + ") in " + url, t); } } // end of while urls } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); }}
这段代码很长,主要就是加载制定文件下一类全名为文件名的文件,然后读取文件内容,将对应的键值对添加到classes中,最后返回。
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getDefaultExtension()具体过程如下:
getExtensionLoader,获取ExtensionLoader实例
getDefaultExtension,调用默认getDefaultExtension
getExtensionClasses,获取所有扩展类
loadExtensionClasses,利用loadFile装置所有扩展类
loadFile,读取文件,加载配置文件中的扩展类,根据键值对装置到classes
以上就是ExtensionLoader具体加载一个类的过程,还有一种自适应的方式动态创建类的过程,下回分解
- Dubbo 源码解析系列一 ExtensionLoader
- dubbo源码解析(一): 扩展点加载(ExtensionLoader)
- Dubbo ExtensionLoader源码解读
- Dubbo源码 SPI实现ExtensionLoader
- Dubbo源码阅读之 ExtensionLoader
- [Dubbo源码剖析]ExtensionLoader机制
- Dubbo系列-3.扩展核心ExtensionLoader
- Dubbo源码分析(五)ExtensionLoader
- dubbo 源码学习笔记 (一) —— ExtensionLoader和URL
- dubbo源码分析-ExtensionLoader发现机制和Adaptive注解应用
- Dubbo ExtensionLoader分析
- dubbo spi extensionloader 插件化
- dubbo 1 ExtensionLoader 插件化
- twemproxy源码解析系列一
- dubbo源码解析
- dubbo源码解析-LoadBalance
- 第一章 Dubbo风格的SPI-ExtensionLoader
- 从ExtensionLoader看Dubbo插件化
- [jQuery知识]jQuery之知识体系
- git pull --rebase
- jdbc学习
- 1622-5 孔富晨 总结《2016年12月6日》 【连续第67天总结】
- 利用从awr中查找好的执行计划来优化SQL
- Dubbo 源码解析系列一 ExtensionLoader
- 滤波器中截止频率的理解
- java动态代理(JDK和cglib)
- Servlet与jquery ajax测试读取返回对象属性
- 好记性不如烂笔头,得把之前的知识整理出来
- ASP.Net MVC开发基础学习笔记(1):走向MVC模式
- Bootstrap学习总结笔记(3)-- 基本样式之表单
- 练习
- node操作mongodb的model不能在多个router里创建的问题