阅读 571

dubbo对SPI扩展实现|周末学习

本文已参与周末学习计划,点击链接查看详情:juejin.cn/post/696572…

关于SPI

可以看之前写的博文,点击链接查看详情:juejin.cn/post/696771…

JDK中的SPI缺点

  1. jdk中spi是一次性把扩展点的实现全部实例化,如果扩展点的实现很多加载很耗时间
  2. 异常无法准确捕捉提示,当扩展点的的某个实现依赖的第三方库不存在,会导致类加载失败,报的错误是找不到扩展点,而不是扩展点加载失败,以及真正的原因

Dubbo对SPI的改进

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

Dubbo中的SPI实现

  1. 在需要扩展点接口使用@SPI注解标识,以前使用的@Extension注解,不过后来因为含义广泛废弃,改用SPI
  2. 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());
 }

复制代码
  1. 返回指定名字的扩展点对象
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;
 }

复制代码
  1. 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具体使用

  1. 以protocol为例,缺省使用dubbo协议,dubbo支持默认spi扩展点
@SPI("dubbo")
public interface Protocol {

//.....省略代码
}

复制代码
  1. SPI对应的配置文件
//com.alibaba.dubbo.rpc.Protocol文件里面的配置
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol

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