前言
其实网上关于Dubbo SPI机制的文章已经数不胜数了,为何笔者还要写这篇文章呢?我想说大概有以下三个原因。
加深一下理解
因为要完全读懂Dubbo的源码,就必须对Dubbo的SPI机制非常熟悉,不然会发现里面的调用关系理不顺,很容易坚持不下去。最近准备在面阿里。
复习一下Dubbo.独特见解来剖析SPI机制
可能会让你们更加容易抓住重点,不仅仅只是停留在代码层面的解析。
SPI设计之初
1.为何要有SPI
Dubbo在设计阶段时考虑的就不仅仅只是一个面向内部的RPC框架,而是想在整个分布式服务领域提供解决方案的,甚至想在国外的RPC领域站稳脚跟。基于这种思想,就必须尽可能少的依赖第三方。我们都知道,Spring早已非常流行,如果Dubbo直接使用Spring来做解耦,得省事得多,但是他们并没有那么做,所以就有了Dubbo自己的SPI,它的核心其实就是一个工厂,根据指定的类型来获取实例。古老的简单工厂模式都是通过if else来根据不同的类型来返回不同的实例,但是Dubbo并没有硬编码,而且结合JDK底层的ServiceLoader机制来实现的,配置文件的修改对调用方不可见,这就是一种解耦。
2.ExtentionLoader和ServiceLoader的区别
既然有了JDK自带的ServiceLoader,那为何还要自己实现一套了,大概有以下几个原因。
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源;
如果扩展点加载失败,连扩展点的名称都拿不到了
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