dubbo源码学习(一)之ExtensionLoader
【转载请注明作者和原文链接,欢迎讨论,相互学习。】
一、前言
ExtensionLoader类,主要是根据扩展点名称来对扩展点接口实现进行的一系列操作,如果获取扩展点接口实现实例、适配类实例、更新实现实例等等。
ExtensionLoader类是dubbo对JDK SPI做了扩展,主要体现在方面:
(1)jdk spi仅仅通过接口类名获取所有实现,而ExtensionLoader则通过接口类名和key值获取一个实现;
(2)Adaptive实现,就是生成一个代理类,这样就可以根据实际调用时的一些参数动态决定要调用的类了。
(3)自动包装实现,这种实现的类一般是自动激活的,常用于包装类,比如Protocol的两个实现类:ProtocolFilterWrapper、ProtocolListenerWrapper。 3、url总线设计 Dubbo为了使得各层解耦,采用了url总线的设计。我们通常的设计会把层与层之间的交互参数做成Model,这样层与层之间沟通成本比较大,扩展起来也比较麻烦。因此,Dubbo把各层之间的通信都采用url的形式。比如,注册中心启动时,参数的url为: registry://0.0.0.0:9090?codec=registry&transporter=netty 这就表示当前是注册中心,绑定到所有ip,端口是9090,解析器类型是registry,使用的底层网络通信框架是netty。
二、代码分析
这边说明如果获取扩展点接口实现实例步骤流程作为说明重点,其他的类似,套路都差不多。具体流程如下:
1.获取ExtensionLoader实例
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 12 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 13 if (loader == null) { 14 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 15 loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 16 } 17 return loader; 18 }
这个方法有如下几个重点:
1)判断是否为扩展点接口
2)是否带有SPI注解
3)该扩展点如果为null,则新建该扩展实现实例并返回。
2.获取指定扩展点名称对应的实例
1 /** 2 * 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}. 3 * 4 * @param name 5 * @return 6 */ 7 @SuppressWarnings("unchecked") 8 public T getExtension(String name) { 9 if (name == null || name.length() == 0) 10 throw new IllegalArgumentException("Extension name == null"); 11 if ("true".equals(name)) { 12 return getDefaultExtension(); 13 } 14 Holder<Object> holder = cachedInstances.get(name); 15 if (holder == null) { 16 cachedInstances.putIfAbsent(name, new Holder<Object>()); 17 holder = cachedInstances.get(name); 18 } 19 Object instance = holder.get(); 20 if (instance == null) { 21 synchronized (holder) { 22 instance = holder.get(); 23 if (instance == null) { 24 instance = createExtension(name); 25 holder.set(instance); 26 } 27 } 28 } 29 return (T) instance; 30 }
这个步骤稍微复杂,我们详细分解一下:
1)9~13行,对扩展点名称进行合法性判断,如果是true,返回默认的扩展点接口实现。
2)14~19行,在cachedInstances中put一个存放扩展点接口实现实例的对象。
3)24行是创建扩展点接口实现的关键,方法:createExtension。
1 private T createExtension(String name) { 2 Class<?> clazz = getExtensionClasses().get(name); 3 if (clazz == null) { 4 throw findException(name); 5 } 6 try { 7 T instance = (T) EXTENSION_INSTANCES.get(clazz); 8 if (instance == null) { 9 EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance()); 10 instance = (T) EXTENSION_INSTANCES.get(clazz); 11 } 12 injectExtension(instance); 13 Set<Class<?>> wrapperClasses = cachedWrapperClasses; 14 if (wrapperClasses != null && wrapperClasses.size() > 0) { 15 for (Class<?> wrapperClass : wrapperClasses) { 16 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 17 } 18 } 19 return instance; 20 } catch (Throwable t) { 21 throw new IllegalStateException("Extension instance(name: " + name + ", class: " + 22 type + ") could not be instantiated: " + t.getMessage(), t); 23 } 24 }
这个方法的关键在于,也就是我们需要的实现类了:Class<?> clazz = getExtensionClasses().get(name);
它会从META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/这个三个目录下扫描所有的扩展点信息,存放到extensionClasses这个Map<String, Class<?>> 里边,
然后我们根据扩展点接口名称就可以获取到扩展点接口实现实例了,具体代码如下。
1 private Map<String, Class<?>> getExtensionClasses() { 2 Map<String, Class<?>> classes = cachedClasses.get(); 3 if (classes == null) { 4 synchronized (cachedClasses) { 5 classes = cachedClasses.get(); 6 if (classes == null) { 7 classes = loadExtensionClasses(); 8 cachedClasses.set(classes); 9 } 10 } 11 } 12 return classes; 13 }
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; }