本文已参与周末学习计划,点击链接查看详情:juejin.cn/post/696572…
关于SPI
可以看之前写的博文,点击链接查看详情:juejin.cn/post/696771…
JDK中的SPI缺点
- jdk中spi是一次性把扩展点的实现全部实例化,如果扩展点的实现很多加载很耗时间
- 异常无法准确捕捉提示,当扩展点的的某个实现依赖的第三方库不存在,会导致类加载失败,报的错误是找不到扩展点,而不是扩展点加载失败,以及真正的原因
Dubbo对SPI的改进
- 增加了扩展点的默认实现
- 增加了AOP的实现
- 增加了缓存机制,提高了性能
- 配置文件内容改成为key=value形式,这样配置是为了改进上面所说的SPI的第二点缺点,为了将异常信息和配置对应起来
Dubbo中的SPI实现
- 在需要扩展点接口使用@SPI注解标识,以前使用的@Extension注解,不过后来因为含义广泛废弃,改用SPI
- dubbo使用ExtensionLoader.getExtensionLoader(Class type)获取扩展点实例,下面是具体实现
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());
}
复制代码
- 返回指定名字的扩展点对象
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 实现
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扩展点
@SPI("dubbo")
public interface Protocol {
//.....省略代码
}
复制代码
- SPI对应的配置文件
//com.alibaba.dubbo.rpc.Protocol文件里面的配置
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
复制代码
- DubboProtocol
/*
*jdk内部的SPI机制需要通过循环判断才能获取到扩展点实例,而dubbo只需要通过通过key就可以获取扩展点实例
*/
public static DubboProtocol getDubboProtocol() {
if (INSTANCE == null) {
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load
}
return INSTANCE;
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END