博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dubbo spi(续)
阅读量:5977 次
发布时间:2019-06-20

本文共 5856 字,大约阅读时间需要 19 分钟。

hot3.png

之前写了一篇关于dubbo spi的文章,现在看来后面的代码交代的不清楚,今天做一点补充。

dubbo的spi重要的是理解类ExtensionLoader。

这个类的头部作者就交代了这个类要做什么

Dubbo使用的扩展点获取。

自动注入关联扩展点。

自动Wrap上扩展点的Wrap类。

缺省获得的的扩展点是一个Adaptive Instance。

那么究竟是怎么做的我们不妨来看看他的变量在做什么。

dubbo变量里面有两个表示文件夹的变量

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

下文涉及到这三个变量的方法在这里。

LoadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);    loadFile(extensionClasses, DUBBO_DIRECTORY);    LoadFile(extensionClasses, SERVICES_DIRECTORY);

也就是说扩展类extensionClasses都是从这三个文件夹里面获取的。 接下来的两个变量

private static final ConcurrentMap
, ExtensionLoader
> EXTENSION_LOADERS = new ConcurrentHashMap
, ExtensionLoader
>();private static final ConcurrentMap
, Object> EXTENSION_INSTANCES = new ConcurrentHashMap
, Object>();

下文的方法都是从这两个容器获得ExtensionLoader类和ExtensionLoader类实例

接下来是两个变量

private final Class
type; private final ExtensionFactory objectFactory;

一个是该ExtensionLoader类代表的接口类型还有就是生成ExtensionLoader的适配器工厂。

接下来是缓存容器

private final ConcurrentMap
, String> cachedNames = new ConcurrentHashMap
, String>(); private final Holder
>> cachedClasses = new Holder
>>(); private final Map
cachedActivates = new ConcurrentHashMap
(); private volatile Class
cachedAdaptiveClass = null; private final ConcurrentMap
> cachedInstances = new ConcurrentHashMap
>(); private String cachedDefaultName; private final Holder
cachedAdaptiveInstance = new Holder(); private volatile Throwable createAdaptiveInstanceError; private Set
> cachedWrapperClasses;

还有一个容错容器

private Map
exceptions = new ConcurrentHashMap
();

接下来我们看看各个方法都做了什么 1、什么时候生成了这个ExtensionLoader

dubbo代码中很多地方都调用了ExtensionLoader类的方法getExtensionLoader。该方法采用单利模式生成ExtensionLoader类的实例。

public static 
ExtensionLoader
getExtensionLoader(Class
type) { //首先判断出入的类是否为为空和接口 if (type == null) throw new IllegalArgumentException("Extension type == null"); if(!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); }//判断是否打了注解@SPI if(!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } //先从容器中获取 ExtensionLoader
loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type); if (loader == null) { //获取不到生成一个并放入容器。 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader
(type)); loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type); } return loader; } private ExtensionLoader(Class
type) { //生成的时候将类型传入,并生成一个类型为ExtensionFactory的适配器,赋值给objectFactory。 this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }

2、基于类型的适配器类和扩展类是如何被扫描进来的?

在交代变量的时候我们发现了扫描上文件夹下的类集合,那么这些获取扩展类的方法在方法loadExtensionClasses中

// 此方法已经getExtensionClasses方法同步过。    private Map
> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { //获取@SPI里面的值,这个值后来用来获取默认的实现类。 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
> extensionClasses = new HashMap
>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }

这个类的作用只是将三个文件夹里面与type一致的名称和类的映射关系维护到类容器中,我们仔细看看这个类。

private void loadFile(Map
> extensionClasses, String dir) { //获取文件路径,因为dubbo的各个配置文件符合文件夹+接口名称这样的规则 //1获取类加载器 //2、根据类加载器加载文件 //3、遍历文件 //4、将每一行信息按照“=”分隔。 //4.1、获取=右面的类。 //4.2、保证获取的类是否是type接口的实现类。 //4.3、如果类标有@Adaptive,则将这个类放置到适配器类的位置。(适配器类只能是一个) //4.4、如果不是适配器类,则将其其名称和类之间的关系维护到容器cachedNames和extensionClasses中 }

3、dubbo spi重要的就是在初期生成了一个适配器类,用做之后找到真正实现类的中转站。

injectExtension((T) getAdaptiveExtensionClass().newInstance());

1、生成适配器类

2、实例化适配器类

3、如果是工厂类的适配器则injectExtension未做任何事情。如果非工厂类会获取所有set方法的属性值,如果工厂类能够查找到相应的适配器类,则将其注入进来。

4、 寻找扩展点,dubbo spi费了半天的劲儿去实例化了一个适配器,作用还是在生成扩展点起作用。

4.1、获取缺省的扩展点

上文已经交代,在生成ExtensionLoader实例的时候就找到了一个默认的扩展点名称,将其放置到了变量cachedDefaultName中。根据这个名称,我们可以生成默认的扩展点实例。

public T getDefaultExtension() {	    getExtensionClasses();       if(null == cachedDefaultName || cachedDefaultName.length() == 0               || "true".equals(cachedDefaultName)) {           return null;       }       return getExtension(cachedDefaultName);	}

4.2、获取指定名称的扩展点

T getExtension(String name)

4.3、获取指定名称的扩展类

private Class<?> getExtensionClass(String name)

5、编程方式注入扩展点

5.1、编程方式添加新扩展点

public void addExtension(String name, Class<?> clazz)

5.2、编程方式添加替换已有扩展点

public void replaceExtension(String name, Class<?> clazz)

至此,ExtensionLoader类就交代完了,对于这个类的理解将对我们深入理解dubbo源码有一定帮助。

作者hyssop,请君留言批评指正。

转载于:https://my.oschina.net/zjItLife/blog/733915

你可能感兴趣的文章
MongDB修改器
查看>>
mongod开始重启停止脚本
查看>>
selenium webdirver之ruby-grid使用步骤
查看>>
SOAP WebService接口功能自动化测试
查看>>
c#读写文件:概述
查看>>
minikube环境安装
查看>>
稳定高效Linux虚拟主机业务完整解决方案
查看>>
自己写一个简单的模版引擎
查看>>
职业规划
查看>>
RHEL6基础之三RHEL官网获取ISO镜像
查看>>
android第九期 - QQ5.0主窗体resideMenu
查看>>
在“挑战”中成长【我与51CTO一“七”成长】
查看>>
AWS - 创建并远程访问windows实例
查看>>
Android Studio 第七十九期 - Android 支付宝数字递增显示
查看>>
马云的教、马云的会、马云的墓
查看>>
由文档那些事儿引发的思考 - 领导,您该反思了
查看>>
整体集群配置
查看>>
Azure Stack PaaS-网站(WebApp)背后是什么原理?
查看>>
新浪微博如何更近一步?
查看>>
2016! 新年快乐! 猴年快乐!
查看>>