dubbo源码分析(二):dubbo的SPI扩展实现

JDK中的SPI缺点

  1. jdk中spi是一次性把扩展点的实现全部实例化,如果扩展点的实现很多加载很耗时间

  2. 异常无法准确捕捉提示,当扩展点的的某个实现依赖的第三方库不存在,会导致类加载失败,报的错误是找不到扩展点,而不是扩展点加载失败,以及真正的原因

dubbo对SPI的改进

  1. 增加了扩展点的默认实现
  2. 增加了AOP的实现
  3. 增加了缓存机制,提高了性能
  4. 配置文件内容改成为key=value形式,这样配置是为了改进上面所说的SPI的第二点缺点,为了将异常信息和配置对应起来

dubbo SPI实现

  • 在需要扩展点接口使用@SPI注解标识,以前使用的Extension注解,不过后来因为含义广泛废弃,改用SPI
  • dubbo使用ExtensionLoader.getExtensionLoader(Class type)获取扩展点实例,下面是具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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!");
}

//获取扩展点,EXTENSION_LOADERS 是一个map,key是扩展点接口类型,value是一个ExtensionLoader对象
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) {
this.type = type;

//扩展点类型如果是ExtensionLoader,返回null,否则返回扩展装饰类
objectFactory = (type == ExtensionFactory.class ? null
: ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
  • 返回指定名字的扩展点对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public T getExtension(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}

//从缓存中获取扩展点对象
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.set(instance);
}
}
}
return (T) instance;
}
  • cachedInstances 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

public class Holder<T> {

/**
* value线程可见性
*/
private volatile T value;

public void set(T value) {
this.value = value;
}

public T get() {
return value;
}

}

dubbo对spi具体使用

  • 以protocol为例,缺省使用dubbo协议,dubbo支持默认spi扩展点

    1
    2
    3
    4
    5
    @SPI("dubbo")
    public interface Protocol {

    //.....省略代码
    }
  • 配置文件

1
2
//com.alibaba.dubbo.rpc.Protocol文件里面的配置
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
  • DubboProtocol
1
2
3
4
5
6
7
8
9
10

/*
*jdk内部的SPI机制需要通过循环判断才能获取到扩展点实例,而dubbo只需要通过通过key就可以获取扩展点实例
*/
public static DubboProtocol getDubboProtocol() {
if (INSTANCE == null) {
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load
}
return INSTANCE;
}

dubbo 对IOC和AOP的增强