什么是 SPI
SPI全称是Service provider interface,顾名思义就是服务提供接口,本质上就是以接口的形式提供服务。
SPI目标就是对应用提供可拔插的服务,应用会首先提供一个接口进行扩展。我们只需要在META-INF下建立一个service/接口全限定名 文件。文件内容就是实现类,可以通过编程发现classpath下所有实现的这个接口的类并将它们全部加载到JVM中。

MySQL的驱动包就利用了这个规则,java.sql.Driver是java提供的一个接口。mysql驱动包负责实现这个类,并在service下通过SPI机制试图让Java发现并加载它。所以像之前Class.forName(“xxx”);现在就不必要编写了。
Dubbo的SPI
上面所说的SPI是Java底层提供的功能,但这种SPI的设计与Dubbo所需要的功能有一些偏差,Dubbo就自己实现了一套属于自己的SPI。
具体实现和JavaSPI基本一样,有以下几点不一样
- 接口文件包名不一样
- 文件中是已key-value存储,不是简单的字符串

调用
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol protocol = extensionLoader.getExtension("http");
System.out.println(protocol);
- 获取ExtensionLoader实例
- 通过ExtensionLoader#getExtension方法获取拓展类对象
Dubbo getExtensionLoader源码分析
此方法主要作用是获取ExtensionLoader实例,ExtensionLoader这个实例具是某个接⼝的扩展点加载器,可以⽤来加载某个扩展点实例。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// 创建一个缓存
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
// 缓存没命中,设置进缓存,这里可以看出ExtensionLoader是按类型划分
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
Dubbo getExtension源码分析
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 获取默认扩展类
if ("true".equals(name)) {
return getDefaultExtension();
}
// holder封装,便于加锁
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
// DCL
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建扩展点实例对象
// ======================重要方法=================
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
createExtension
private T createExtension(String name) {
// 获取SPI接口的所有实现类,并通过name获取指定实现类
// 这里就是找哪些包下面有SPI接口实现类的地方 ===============重要方法==========
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);
// AOP,cachedWrapperClasses无序
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
}
}
这个方法相当重要,在这其中共有4大逻辑
- getExtensionClasses获取拓展类
- 创建实例
- 依赖注入
- Wrapper对象包裹实例
getExtensionClasses获取拓展类
private Map<String, Class<?>> getExtensionClasses() {
// DCL
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加载、解析文件 Map
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
// cache接口默认的扩展类,就是在接口上标注了@SPI注解的value
cacheDefaultExtensionName();
// 加载不同路径下的文件
Map<String, Class<?>> extensionClasses = new HashMap<>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
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;
}
loadDirectory
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
String fileName = dir + type;
try {
// 根据文件中的内容得到urls, 每个url表示一个扩展
Enumeration<java.net.URL> urls;
// 找到加载当前类的类加载器,这里什么加载器都有可能,自定义加载器和应用程序类加载器
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
// 通过类加载器从文件名中找到路径
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
// 遍历url进行加载,把扩展类添加到extensionClasses中
// 这个方法主要就是文件内容的读取,并将文件内容每一行数据进行字符串切割提取出name和类全限定名,然后执行loadClass加载类并传入到extensionClasses中
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
loadResource->loadClass
加载类并传入到extensionClasses中
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
// native方法,判断此类是不是type的实现类,不是就要报错
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
// 当前接口手动指定了Adaptive类
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
// 判断是不是一个Wrapper类,判断方法是是不是有一个构造方法参数只有一个,类型是接口
} else if (isWrapperClass(clazz)) {
// 是一个Wrapper类
cacheWrapperClass(clazz);
} else {
// 需要有无参的构造方法
clazz.getConstructor();
// 在文件中没有name,但是在类上指定了Extension的注解上指定了name
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
// 缓存一下被Activate注解了的类
cacheActivateClass(clazz, names[0]);
// 有多个名字
for (String n : names) {
// clazz: name
cacheName(clazz, n);
// name: clazz
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
injectExtension依赖注入
这里的依赖注入和Spring的依赖注入类似
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
// 判断方法是否有set特性
if (!isSetter(method)) {
continue;
}
// 如果方法上标注了DisableInject注解表示不使用依赖注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 判断set方法中的参数类型是否为基本类型,基本类型就跳过
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 得到setXxx中的xxx
String property = getSetterProperty(method);
// 根据参数类型或属性名,从objectFactory中获取到对象,然后调用set方法进行注入
// 这里的objextFactory 是 AdaptiveExtensionFactory
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 调用set方法注入
method.invoke(instance, object);
}
} catch (Exception e) {
}
}
} catch (Exception e) {
}
return instance;
}
@Override
public <T> T getExtension(Class<T> type, String name) {
// 遍历两个ExtensionFactory( SpringExtensionFactory,SpiExtensionFactory),从ExtensionFactory中得到实例
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
factory.getExtension&&createAdaptiveExtensionClass
factory 有2种,一种是SpringExtensionFactory还有一种是SpiExtensionFactory,SpringExtensionFactory很简单就是去Spring容器中获取这里我们具体来看下SpiExtensionFactory是如何进行依赖注入的
SpiExtensionFactory->getAdaptiveExtension->createAdaptiveExtension->getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 获取当前接口的所有扩展类
getExtensionClasses();
// 缓存了@Adaptive注解标记的类
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 如果某个接口没有手动指定一个Adaptive类,那么就自动生成一个Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
// cachedDefaultName表示接口默认的扩展类,,code表示代理类的具体代码
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
// 找到自己的类加载器
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 编译代码生成代理类
return compiler.compile(code, classLoader);
}

从编译的代码结果中分析,我们发现使用依赖注入会存在一些条件
- 必须有参数
- 必须在参数中带着Url类型参数
Dubbo中的AOP
在loadResource->loadClass->cacheWrapperClass中缓存了当前type的Wrapper类
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
这段代码明显可以看出把原实例注入到wrapper方法的构造方法中,并将wrapper对象返回,而且这段代码是遍历的,所以说如果有多个wrapper就会进行多层包裹,假设有2个wrapper就变成了wrapper包裹wrapper包裹原实例

