简介
SPI(service provider interface)是一种服务发现机制,通过加载指定路径下配置文件中的实现类,达到运行时用实现动态替换接口的目的。SPI常常用于扩展应用的功能,Dubbo就是通过SPI来加载所有的组件,因此用户可以很方便地扩展Dubbo的功能。Dubbo SPI是在JDK SPI的基础上发展过来的,相比之下,它做了一些改进和优化。
改进(相比JDK SPI)
- JDK SPI会一次性加载所有扩展点实现,如果有的实现初始化耗时,有的没用上,会比较浪费资源。Dubbo SPI在配置文件中为每个实现类指定key,可通过key去加载对应的实现类。
- JDK SPI扩展实现加载失败时,会将异常信息吃掉,导致很难定位失败原因。Dubbo则会在异常抛出前写到日志中,同时也会正常抛出异常。
- Dubbo SPI提供了对IOC和AOP的支持,体现在扩展点自动包装和自动装配。
约定
在扩展类的jar包中,将扩展点的配置文件放在META-INF/dubbo/接口全限定名,文件内容为配置名=实现类全限定名,多个实现类用换行符分隔。
示例
以扩展Dubbo的Filter接口为例,现在实现类的jar包META-INF/dubbo/下创建名为com.alibaba.dubbo.rpc.Filter的文件。文件内容为
xxx=com.alibaba.xxx.XxxFilter实现类
package com.alibaba.xxx;
import org.apache.dubbo.rpc.Filter;
public class XxxFilter implements Filter{ // ...
}配置过滤使用指定实现类
dubbo.provider.filter=xxx扩展点特性
自动包装
加载某个扩展点实现时,如果发现存在参数为扩展点的构造函数,那么这个扩展点实现就是Wrapper类。
Wrapper类内容
package com.alibaba.xxx;
import org.apache.dubbo.rpc.Protocol;
public class XxxProtocolWrapper implements Protocol {
Protocol impl;
public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
// ...
}Wrapper类持有了真正的扩展点实现类,主要作用是封装扩展点的公用逻辑,类似AOP。
自动注入
如果扩展实现类的成员属性也是一个扩展点,那么ExtensionLoader会加载并注入这个依赖的扩展点。这是Dubbo SPI的IOC特性,依赖扩展点通过setter方法进行注入。
自适应
在注入一个扩展点时,当一个扩展点有多个实现类,在程序运行前可能我们并不知道要加载哪个实现类。自适应扩展则支持在运行时根据URL参数动态加载实现。Dubbo提供@Adaptive注解来实现自适应。@Adaptive如果作用于类上,那么标明该类是一个自适应扩展类。作用于方法上,则会运行时生成一个代理类来进行转发,由代理类根据URL中的传参去加载真正的实现。
public interface Transporter {
@Adaptive({"server", "transport"})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
@Adaptive({"client", "transport"})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}自动激活
对于集合类扩展点,比如Filter、xxxListener,可以同时加载多个实现。此时可以使用自动激活来实现。
源码分析
加载扩展点时序图

在我们加载扩展实现前会先获取一个使用getExtensionLoader方法获取一个ExtensionLoader实例。
getExtensionLoader
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 interface!");
} else if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
} else {
ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
}
return loader;
}
}这个方法比较简单,先对参数扩展点类型type进行检查,然后去缓存里面获取ExtensionLoader实例。缓存没有的话,新建一个放入缓存,然后返回。
下面是getExtension方法,该方法是加载实现类的入口。
getExtension
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
// 获取默认的拓展实现类
return getDefaultExtension();
}
// Holder,顾名思义,用于持有目标对象
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
// 双重检查
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建拓展实例
instance = createExtension(name);
// 设置实例到 holder 中
holder.set(instance);
}
}
}
return (T) instance;
}该方法先检查缓存,缓存未命中则用createExtension创建扩展对象。
createExtension
private T createExtension(String name) {
// 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 通过反射创建实例
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 向实例中注入依赖
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
// 循环创建 Wrapper 实例
for (Class<?> wrapperClass : wrapperClasses) {
// 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
// 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
instance = injectExtension(
(T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("...");
}
}上面的方法可以分为以下几个步骤:
- 通过getExtensionClasses获取配置文件中的所有实现类
- 反射创建实现类
- 注入依赖的其他扩展点(IOC)
- 创建Wrapper类实例并返回(AOP)
在getExtensionClasses方法中主要是去指定路径下读取和解析配置文件,加载实现类。根据扩展类的类型给分别给cachedClasses、cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames赋值。
injectExtension方法是Dubbo SPI的IOC实现,通过setter方法注入依赖。
injectExtension
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
// 遍历目标类的所有方法
for (Method method : instance.getClass().getMethods()) {
// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
// 获取 setter 方法参数类型
Class<?> pt = method.getParameterTypes()[0];
try {
// 获取属性名,比如 setName 方法对应属性名 name
String property = method.getName().length() > 3 ?
method.getName().substring(3, 4).toLowerCase() +
method.getName().substring(4) : "";
// 从 ObjectFactory 中获取依赖对象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 通过反射调用 setter 方法设置依赖
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method...");
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}因为依赖的扩展点由ExtensionFactory创建,所以首先判断objectFactory是否为null。然后反射获取实现类的所有方法,遍历过滤setter方法,得到属性名称。通过objectFactory的genExtension方法获取属性名称对应的扩展实现,最后通过反射调用setter方法注入依赖。
自适应扩展机制

getAdaptiveExtension
public T getAdaptiveExtension() {
// 从缓存中获取自适应拓展
Object instance = cachedAdaptiveInstance.get();
if (instance == null) { // 缓存未命中
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建自适应拓展
instance = createAdaptiveExtension();
// 设置自适应拓展到缓存中
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: ...");
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: ...");
}
}
return (T) instance;
}获取自适应扩展类的入口,首先检查缓存,缓存未命中调用createAdaptiveExtension方法创建一个自适应扩展实例,最后缓存起来并返回。
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
// 获取自适应拓展类,并通过反射实例化
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension ...");
}
}该方法首先调用getAdaptiveExtensionClass方法得到自适应扩展类的Class对象,然后通过反射实例化,最后调用injectExtension注入依赖。如果@Adaptive注解是作用在方法上,那么运行时自动生成的代理类的属性里面是不会有其他扩展点,也就不用注入依赖。这是主要是针对@Adaptive注解修饰在类上的情况。
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 通过 SPI 获取所有的拓展类
getExtensionClasses();
// 检查缓存,若缓存不为空,则直接返回缓存
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 创建自适应拓展类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}先通过getExtensionClasses获取配置文件配置的所有实现类,然后检查缓存,缓存不为空则返回。缓存为空,调用createAdaptiveExtensionClass创建自适应扩展类。
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 构建自适应拓展代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
// 获取编译器实现类
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 编译代码,生成 Class
return compiler.compile(code, classLoader);
}
这个方法首先会生成代理类的源代码,然后使用Complier编译器编译得到代理类的Class。至于
createAdaptiveExtensionClassCode如何生成源码,代码较长不做分析,可以参考官方文档dubbo.apache.org/zh-cn/docs/…。
总结
Dubbo SPI是Dubbo的一大特性,它使得Dubbo具有很好的灵活性、便于扩展,对开发者非常友好。其实现本质上也就是规范扩展的步骤和方法,加载扩展点。同时还引入了自动包装、自动注入、自适应、自动激活等特性功能,以满足更多的业务场景。源码部分除了生成自适应代理类比较长,其它都不难理解。