Dubbo SPI

Dubbo 源码阅读 gitee:https://gitee.com/hitol/dubbo-source/tree/read_2.7.5/

Java SPI

SPI 是服务发现机制,为某个接口寻找服务的实现。
是 JDK 6 的特性,ServiceLoader。
ServiceLoader启动时会到 resource/META-INF/services/ 目录下读取以 实现类的全限定名 命名的文件。

程序通过 ServiceLoader 动态加载实现类模块,通过扫描 META-INF/services 目录下的配置文件找到实现类的全限定名,把类加载到JVM;

demo:

// 一个接口,两个实现类
public interface Fruit {

	void printName();

}


public class Apple implements Fruit {
	@Override
	public void printName() {
		System.out.println("Apple");
	}
}

public class Orange implements Fruit {
	@Override
	public void printName() {
		System.out.println("Orange");
	}
}

在 src/main/resources/META-INF/services 下新建文件 xxx.xxx.xx.Fruit,这个文件名就是Fruit接口的全限定名,
文件内容:

com.hitol.service.impl.Apple
com.hitol.service.impl.Orange

文件中的内容是 Apple、Orange类的全限定名,可以配置一个也可以配置多个。

public static void main(String[] args) {
	ServiceLoader<Fruit> fruits = ServiceLoader.load(Fruit.class);

	for (Fruit fruit : fruits) {
		fruit.printName();
	}
}

会依次调用xxx.xxx.xx.Fruit文件中配置的具体实现类。


应用场景:各种数据库连接

Dubbo SPI

Dubbo SPI 扩展点加载,从 JDK 标准的 SPI 扩展点机制加强而来。
加载的路径约定为: resource/META-INF/dubbo/接口全限定名
增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

将上面例子中的xx.Fruit文件移到 resource/META-INF/dubbo/ 目录下,同时需要在 Fruit 类上加 Dubbo 注解 @SPI。
文件内容

orange=com.hitol.util.services.impl.Orange
apple=com.hitol.util.services.impl.Apple

demo:

public static void main(String[] args) {
	ExtensionLoader<Fruit> extensionLoader = ExtensionLoader.getExtensionLoader(Fruit.class);
	Fruit apple = extensionLoader.getExtension("apple");
	Fruit orange = extensionLoader.getExtension("orange");

	apple.printName();
	orange.printName();
}

Dubbo SPI 的实现封装在 ExtensionLoader 中。

Dubbo 中几乎到处可见 SPI 机制的应用,比如说,

  1. 反射创建默认实现类,org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass
  2. dubbo 协议

Dubbo SPI 的实现

ExtensionLoader.getExtensionLoader
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())
extensionLoader.getExtension

ExtensionLoader.getExtensionLoader

SPI-1-getExtensionLoade
SPI-1-getExtensionLoade

这个方法的逻辑比较简单,是否有 SPI 注解,缓存中没有的话就 new 一个 ExtensionLoader 出来,不过在 new 的时候会去获取 objectFactory 属性的值。第一次 new 的话,会加载 ExtensionFactory,并获取它的默认实现。

private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

getAdaptiveExtension()

SPI 修饰的接口的实现类中,获取带有 @Adaptive 注解的实现。
在同一个接口的实现类中,@Adaptive 注解只能修饰其中的一个实现类,表示该接口的默认实现。

getAdaptiveExtension 方法就是获取这个默认的实现类。

SPI(value= "apple") 注解上可以指定 默认的实现类。

extensionLoader.getExtension

public T getExtension(String name) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    // 获取holder,相当于持有对象的锁
    // 从缓存map cachedInstances 中获取,获取不到就new
    final Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    // 双重检测单例模式
    // 此处这个锁对象是holder,而不是整个class对象,锁粒度更小,并发更高
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

getExtension
getExtension

拓展类对象的获取过程,都是先从缓存中查找,没有在新建对象。最重要的实现逻辑是在 createExtension 方法中。

createExtension

private T createExtension(String name) {
        // 先从cachedClasses 缓存map中获取类对象,获取不到则new
        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);
            }
            // IOC 注入
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
                
            }
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
=========================================
private Map<String, Class<?>> getExtensionClasses() {
    // 缓存
    Map<String, Class<?>> classes = cachedClasses.get();
    // 双重检查单例模式
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                // 加载所有的扩展点对象,然后放到缓存中
                // SpiExtensionFactory
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
=========================================
private Map<String, Class<?>> loadExtensionClasses() {
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();
    // internal extension load from ExtensionLoader's ClassLoader first . 先加载 /internal 目录下的扩展点
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
    // replace("org.apache", "com.alibaba") 替换包名这个骚操作
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);

    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    return extensionClasses;
}

wrapper 类

wrapper 相当于 AOP 动态代理生成的 代理对象。

一个类它是 wrapper 类的条件:

  1. 实现某个接口
  2. 类中只有一个属性变量,是该接口的实例
  3. 只有包含这个属性变量的构造方法。
/**
 * test if clazz is a wrapper class
 * <p>
 * which has Constructor with given class type as its only argument
 */
private boolean isWrapperClass(Class<?> clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false;
    }
}
Fruit 的 wrapper 类
public class FruitWrapper implements Fruit {
	private Fruit fruit;

	public FruitWrapper(Fruit fruit) {
		this.fruit = fruit;
	}

	@Override
	public void printName() {
		System.out.println("this is wrapper");
		fruit.printName();
	}
}

---

文件内容

orange=com.hitol.util.services.impl.Orange
apple=com.hitol.util.services.impl.Apple
com.hitol.util.services.impl.FruitWrapper

---

ExtensionLoader<Fruit> extensionLoader = ExtensionLoader.getExtensionLoader(Fruit.class);
Fruit apple = extensionLoader.getExtension("apple");

apple.printName();

---
输出内容:
this is wrapper
Apple

输出内容:

this is wrapper
Apple

https://dubbo.apache.org/zh/docs/v2.7/dev/spi/
https://dubbo.apache.org/zh/docs/v2.7/dev/source/dubbo-spi/

2021/2/1 posted in  Java dubbo