文档章节

dubbo spi(续)

hyssop
 hyssop
发布于 2016/08/17 11:37
字数 1357
阅读 66
收藏 4

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

http://my.oschina.net/zjItLife/blog/530923

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<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

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

接下来是两个变量

 private final Class<?> type;
 private final ExtensionFactory objectFactory;

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

接下来是缓存容器

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
    
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>();

    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();

    private volatile Class<?> cachedAdaptiveClass = null;

    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

    private String cachedDefaultName;

    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    private volatile Throwable createAdaptiveInstanceError;

    private Set<Class<?>> cachedWrapperClasses;

还有一个容错容器

private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

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

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

  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!");
        }
//判断是否打了注解@SPI
        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;
    }

    private ExtensionLoader(Class<?> type) {
    //生成的时候将类型传入,并生成一个类型为ExtensionFactory的适配器,赋值给objectFactory。
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

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

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

  // 此方法已经getExtensionClasses方法同步过。
    private Map<String, Class<?>> 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<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

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

    private void loadFile(Map<String, Class<?>> 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,请君留言批评指正。

© 著作权归作者所有

hyssop
粉丝 20
博文 102
码字总数 111521
作品 0
昌平
程序员
私信 提问
动手学dubbo之Container与SPI

在动手学dubbo之初体验一文中我们了解了dubbo的架构,接下来的几篇文章我会根据阅读Quick Start里面的demo源码来深入学习dubbo的实现。这一篇主要学习Container的原理、实现和作用。 一、从启...

ginobefun
2017/07/13
0
0
Dubbo源码分析(7):SPI扩展机制剖析

我们都是知道一个合格的开源框架对于扩展的支持都要是相当弹性的,Dubbo 也不例外。Dubbo采用微内核+插件体系,使得设计优雅,扩展性强。Dubbo的扩展机制是基于SPI思想来实现的,但是并没有采...

郑加威
2018/09/28
88
0
Dubbo源码分析(6):Dubbo内核实现之基于SPI思想Dubbo内核实现

SPI接口定义 定义了@SPI注解 只有在接口打了@SPI注解的接口类才会去查找扩展点实现 会依次从这几个文件中读取扩展点 META-INF/dubbo/internal/ //dubbo内部实现的各种扩展都放在了这个目录了...

郑加威
2018/09/24
61
0
探究Dubbo的ExtensionLoader

一定要对比JDK的SPI机制,才能更好的理解Dubbo的SPI实现! 扩展点:即接口扩展:扩展点的实现,即接口的实现类 获取扩展点的ServiceLoader实例 通过扩展点的ServiceLoader实例,完成扩展的方...

向码而生
2018/09/03
152
0
聊聊Dubbo - Dubbo可扩展机制实战

摘要: 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。 1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能...

阿里云云栖社区
2018/06/04
151
1

没有更多内容

加载失败,请刷新页面

加载更多

Mybatis Plus删除

/** @author beth @data 2019-10-17 00:30 */ @RunWith(SpringRunner.class) @SpringBootTest public class DeleteTest { @Autowired private UserInfoMapper userInfoMapper; /** 根据id删除......

一个yuanbeth
今天
4
0
总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
今天
5
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
今天
4
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部