Dubbo系列之SPI拓展点源码分析

Dubbo系列之自定义SPI协议拓展点_codain的博客-CSDN博客Dubbo系列之自定义SPI协议拓展点https://blog.csdn.net/qq_38377525/article/details/123002941这上面的链接中,讲解了Dubbo的SPI机制实现步骤,接下来我们要讲解一下Dubbo是如何实现SPI或者说SPI运行原理是怎么样的,正好我们也可以学习一下Dubbo框架的底层设计


上面链接中最后调用SPI的时候,使用的是getExtension(),那么我们就从这个方法出发,来分析一下Dubbo中是如何实现SPI的

首先,这段代码是有两层的:

第一层:

是我们通过getExtensionLoader获得一个ExtensionLoader实例,然后通过getExtension()方法来获得指定key的拓展点,我们在这一层进一步分析

1、ExtensionLoader.getExtensionLoader

这个方法用于返回一个ExtensionLoader实例,他的逻辑如下:

①先从缓存中获取与拓展类对应的ExtensionLoader,可以查询ExtensionLoader这个类

②如果未命中缓存,就创建一个实例,保存到EXTENSION_LOADERS集合中缓存起来

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    //如果没有拓展点类,就抛异常
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    //如果入参的类不是接口类,抛异常
    } else if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    //如果没有使用@SPI注解,就抛异常
    } else if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    } else {
        //先读取缓存
        ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        if (loader == null) {
            //如果缓存是null,就需要创建实例,并缓存
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
            loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
}

③在ExtensionLoader构造方法中,初始化一个工厂类

private ExtensionLoader(Class<?> type) {
    this.type = type;
    this.objectFactory = type == ExtensionFactory.class ? null : (ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();
}

2、getExtension()

这个方法是根据指定的名称,来获得对应的拓展点并返回,比如getExtension()的入参如果是mysqlDriver,那么返回的实现类就是MysqlDriver

①name入参用于判断参数,如果name=true,就会返回一个默认的拓展实现

②创建一个Holder对象,用户缓存该拓展点的实例

③如果缓存不存在,就会通过createExtension(name)来创建一个拓展点

public T getExtension(String name) {
    //如果没有拓展点,就报错
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    //如果是true,就返回默认的拓展点
    } else if ("true".equals(name)) {
        return this.getDefaultExtension();
    } else {
        //创建Holder对象
        Holder<Object> holder = this.getOrCreateHolder(name);
        //获取缓存数据
        Object instance = holder.get();
        if (instance == null) {
            synchronized(holder) {
                instance = holder.get();//源码代码重复了
                if (instance == null) {
                    //如果缓存不存在,就需要创建一个拓展点
                    instance = this.createExtension(name);//看下方代码剖析
                    holder.set(instance);
                }
            }
        }
        return instance;
    }
}

3、createExtension(name)创建拓展点源码如下:

    private T createExtension(String name) {
        //返回拓展类
        Class<?> clazz = (Class)this.getExtensionClasses().get(name);//看下一段源码片段
        if (clazz == null) {
            throw this.findException(name);
        } else {
            try {
                //获取缓存中该类的实例
                T instance = EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    //ruguo 
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = EXTENSION_INSTANCES.get(clazz);
                }
                //依赖注入
                this.injectExtension(instance);
                //包装
                Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                Class wrapperClass;
                if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                    //循环注入,利用到了装饰器模式
                    for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                        wrapperClass = (Class)var5.next();
                    }
                }
                //返回实例
                return instance;
            } catch (Throwable var7) {
                throw new IllegalStateException("Extension instance (name: " + name + ", class: " + this.type + ") couldn't be instantiated: " + var7.getMessage(), var7);
            }
        }
    }

上面是第一层的代码,其实是加载拓展类的关键实现,其他部分都是不主要但不可少的功能,第二层就要从依赖注入和Wrapper来分析了

第二层:

4、getExtensionClasses()方法直接返回一个Map集合

key和value对应的就是resources下的文件的key和value

    private Map<String, Class<?>> getExtensionClasses() {
        //获取缓存中的拓展类
        Map<String, Class<?>> classes = (Map)this.cachedClasses.get();
        //如果没有缓存数据
        if (classes == null) {
            Holder var2 = this.cachedClasses;
            synchronized(this.cachedClasses) {
                classes = (Map)this.cachedClasses.get();
                if (classes == null) {
                    //调用loadExtensionClasses重新加载拓展类
                    classes = this.loadExtensionClasses();//看下一段代码分析
                    this.cachedClasses.set(classes);
                }
            }
        }

        return classes;
    }

Dubbo的代码其实大家不难看出,基本都是先访问缓存,没有就通过loadExtensionClasses来加载,那这个方法是怎么实现得嘞,继续看

5、loadExtensionClasses()

private Map<String, Class<?>> loadExtensionClasses() {
        //获取当前type接口默认的拓展类并缓存
        this.cacheDefaultExtensionName();
        Map<String, Class<?>> extensionClasses = new HashMap();
        //解析并获取指定路径下的拓展点文件
        this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName());
        this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName().replace("org.apache", "com.alibaba"));
        this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName());
        this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName().replace("org.apache", "com.alibaba"));
        this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName());
        this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
}

其中的loadDirectory()方法大家也能看出来是什么意思,就是从指定的这几个目录下,找到入参进来的type对应的文件,解析文件内容之后加载并保存到extensionClasses集合中,第一行的cacheDefaultExtensionName()这个方法也很简单,但是逻辑稍微偏业务一点,需要再对他分析一波

6、cacheDefaultExtensionName()

private void cacheDefaultExtensionName() {
        //获得入参的type类上面的注解@SPI
        SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            //获取拓展点文件的内容value
            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 " + this.type.getName() + ": " + Arrays.toString(names));
                }
                //保存到cachedDefaultName属性中
                if (names.length == 1) {
                    this.cachedDefaultName = names[0];
                }
            }
        }

    }

@SPI注解默认值是dubbo,只是没有显式出来,全称是@SPI("dubbo")

 以上就是Dubbo针对SPI的源码实现,其实并不复杂,好好消化一下吧,接下来还会讲到另外一种:自适应拓展点(Adaptive),感兴趣的话,可以关注一下哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值