为了账号安全,请及时绑定邮箱和手机立即绑定

Dubbo源码剖析之SPI机制<一>

标签:
Premiere

前言

其实网上关于Dubbo SPI机制的文章已经数不胜数了,为何笔者还要写这篇文章呢?我想说大概有以下三个原因。

  1. 加深一下理解
    因为要完全读懂Dubbo的源码,就必须对Dubbo的SPI机制非常熟悉,不然会发现里面的调用关系理不顺,很容易坚持不下去。

  2. 最近准备在面阿里。
    复习一下Dubbo.

  3. 独特见解来剖析SPI机制
    可能会让你们更加容易抓住重点,不仅仅只是停留在代码层面的解析。

SPI设计之初

1.为何要有SPI

Dubbo在设计阶段时考虑的就不仅仅只是一个面向内部的RPC框架,而是想在整个分布式服务领域提供解决方案的,甚至想在国外的RPC领域站稳脚跟。基于这种思想,就必须尽可能少的依赖第三方。我们都知道,Spring早已非常流行,如果Dubbo直接使用Spring来做解耦,得省事得多,但是他们并没有那么做,所以就有了Dubbo自己的SPI,它的核心其实就是一个工厂,根据指定的类型来获取实例。古老的简单工厂模式都是通过if else来根据不同的类型来返回不同的实例,但是Dubbo并没有硬编码,而且结合JDK底层的ServiceLoader机制来实现的,配置文件的修改对调用方不可见,这就是一种解耦。

2.ExtentionLoader和ServiceLoader的区别

既然有了JDK自带的ServiceLoader,那为何还要自己实现一套了,大概有以下几个原因。

  1. JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源;

  2. 如果扩展点加载失败,连扩展点的名称都拿不到了

  3. ServiceLoader不够灵活。

ExtensionLoader源码分析

在分析之前,我先罗列一下ExtensionLoader涉及到的一些注解、类。

  • ExtensionFactory

@SPIpublic interface ExtensionFactory {    /**
     * Get extension.
     * 
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);
}

可知,ExtensionFactory是获取扩展的工厂,根据指定的Class类型和名字,即可获取对应的实例。

  • @Activate

/**
 * Activate
 * 对于可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。
 */@Documented@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})public @interface Activate {    /**
     * Group过滤条件
     */
    String[] group() default {};    /**
     * Key过滤条件
     */
    String[] value() default {};    /**
     * 排序信息,可以不提供。
     */
    String[] before() default {};    /**
     * 排序信息,可以不提供。
     */
    String[] after() default {};    /**
     * 排序信息,可以不提供。
     */
    int order() default 0;
}

先不做多解释,从字面意思上来看,应该是获取相关激活的扩展(getActivateExtension)

  • @Adaptive

/**
 * 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
 */@Documented@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})public @interface Adaptive {    
    /**
     * 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
     */
    String[] value() default {};
}

其实就是获取对应的适配类,只需要在类上、方法上、参数中指定即可。

  • @Extension

/**
 * 扩展点接口的标识。
 */@Deprecated@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface Extension {    /**
     * @deprecated
     */
    @Deprecated
    String value() default "";
}

Extension("mina")加载失败,当用户配置使用mina时,就会报找不到扩展点,
而不是报加载扩展点失败,以及失败原因。该方法已设置成@Deprecated

  • @SPI

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface SPI {    /**
     * 缺省扩展点名。
     */
    String value() default "";

}

扩展点接口的标识,如果要利用Dubbo的SPI机制,接口必须要加@SPI。

ExtensionLoader的初始化过程

  @SuppressWarnings("unchecked")    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 static <T> boolean withExtensionAnnotation(Class<T> type) {        return type.isAnnotationPresent(SPI.class);
    }

ExtensionLoader.getExtensionLoader()被调用的时候才被初始化,初始化之前会对type进行非空校验、接口限定、type必须加了@SPI注解.然后从EXTENSION_LOADERS判断是否已经加载过,如果已经加载过,则直接返回,否则直接创建一个ExtensionLoader(type)对象,然后返回。
我们先来看一下ExtensionLoader的构造函数:

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



作者:jerrik
链接:https://www.jianshu.com/p/35eb72ec564b


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消