Zero preliminary preparation
0 FBI WARNING
The article is very long and winding.
1 version
JDK version: Adoptopenjdk 14.0.1
IDE : idea 2020.2
Dubbo version: dubbo 2.7.6
2 Introduction to Spi
Dubbo Spi is the fundamental foundation of Dubbo framework extensibility, and is based on the encapsulation and expansion of jdk spi.
3 Demo
3.1 Interface classes that need to be extended
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI("dubbo") // spi 最主要的注解
public interface SpiDemoService {
void sayHello();
}
3.2 Interface implementation class
No. 1 implementation class:
public class SpiDemoOneServiceImpl implements SpiDemoService {
@Override
public void sayHello() {
System.out.println("hello 1");
}
}
Implementation Class 2:
public class SpiDemoTwoServiceImpl implements SpiDemoService {
@Override
public void sayHello() {
System.out.println("hello 2");
}
}
Test method class:
import org.apache.dubbo.common.extension.ExtensionLoader;
public class SpiTest {
public static void main(String[] args) {
// 获取 loader 工厂类
ExtensionLoader<SpiDemoService> loader
= ExtensionLoader
.getExtensionLoader(SpiDemoService.class);
// 获取实体类
SpiDemoService one = loader.getExtension("one");
// 测试方法
one.sayHello();
}
}
##一getExtensionLoader
back to demo:
ExtensionLoader<SpiDemoService> loader
= ExtensionLoader
.getExtensionLoader(SpiDemoService.class);
1 getExtensionLoader
getExtensionLoader is the core static method of ExtensionLoader, used to obtain ExtensionLoader instance
// org.apache.dubbo.common.extension.ExtensionLoader
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 an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 先尝试获取,如果获取失败就创建一个 ExtensionLoader
// EXTENSION_LOADERS 是一个静态 ConcurrentHashMap,用来存放 loader
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;
}
2 ExtensionLoader constructor
// org.apache.dubbo.common.extension.ExtensionLoader
private ExtensionLoader(Class<?> type) {
// 存储要创建的 loader 的类型
this.type = type;
// objectFactory 是 ExtensionFactory 类型的对象,是用来依赖注入的工厂
objectFactory
= (type == ExtensionFactory.class ?
null : ExtensionLoader
.getExtensionLoader(ExtensionFactory.class)
.getAdaptiveExtension());
}
##二 getAdaptiveExtension
1 getAdaptiveExtension
// org.apache.dubbo.common.extension.ExtensionLoader
public T getAdaptiveExtension() {
// cachedAdaptiveInstance 是一个 Holder 对象,Holder 是对 Object 的包装
// 在此处先尝试获取实例化完成的对象,如果获取不到,就进入加载逻辑
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 如果之前初始化的时候报错了,那么错误会被记录下来并缓存在此处,直接抛出
// 此处的设计应该是减少锁消耗
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
// 双锁验证
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建实例并存储在 Holder 对象里
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
// 如果报错了就会把错误存储起来
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
// 返回对象实例
return (T) instance;
}
2 createAdaptiveExtension
Create an instance object:
// org.apache.dubbo.common.extension.ExtensionLoader
private T createAdaptiveExtension() {
try {
// getAdaptiveExtensionClass().newInstance() 方法会使用 Class 对象的 newInstance() 方法创建一个对象
// injectExtension(...) 则会对创建出来的对象进行依赖注入
return injectExtension((T)getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
3 injectExtension
// org.apache.dubbo.common.extension.ExtensionLoader
private T injectExtension(T instance) {
// objectFactory 是用来依赖注入的 ExtensionFactory
// 如果 objectFactory 为空,就直接返回
// 需要注意的是,只有非 ExtensionFactory 的 loader 才有 objectFactory
if (objectFactory == null) {
return instance;
}
try {
// 轮训实例对象中所有的方法
for (Method method : instance.getClass().getMethods()) {
// 如果方法不是 set 方法就跳过
// 此处可以理解为,dubbo 的 spi 依赖注入需要 set 方法支持
if (!isSetter(method)) {
continue;
}
// 如果方法被标注了 DisableInject 注解就跳过
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 如果方法的参数是原始类型就跳过
// 依赖注入需要使用包装类型
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 反射注入
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
##三ExtensionFactory
ExtensionFactory is a factory used for dependency injection:
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}
This interface has three default implementation classes in Dubbo:
org.apache.dubbo.config.spring.extension.SpringExtensionFactory
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
org.apache.dubbo.common.extension.factory.SpiExtensionFactory
在开发中 SpringExtensionFactory 应该会用的更广泛一些,本示例中此处暂时不展开。
1 AdaptiveExtensionFactory
AdaptiveExtensionFactory is the default factory:
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
// 构造器
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader
= ExtensionLoader
.getExtensionLoader(ExtensionFactory.class);
// AdaptiveExtensionFactory 会将 SpiExtensionFactory 和 SpringExtensionFactory 放置在 factories 列表里
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
// 使用 AdaptiveExtensionFactory 去获取实体类的时候,
// 会调用 spi 或者 spring 的 ext 工厂去尝试获取实体类
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
2 SpiExtensionFactory
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.extension.SPI;
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
// spi 工厂用于解析 spi 注解
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
// 如果传入的 type 是一个被 spi 注释的接口,那么会初始化一个它的 class loader
ExtensionLoader<T> loader
= ExtensionLoader.getExtensionLoader(type);
// 初始化对象
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
##Four getExtension
back to demo:
SpiDemoService one = loader.getExtension("one");
1 getExtension
// org.apache.dubbo.common.extension.ExtensionLoader
public T getExtension(String name) {
// 非空验证,忽略
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 默认机制,会去找名称为 dubbo 的实例
// 一般用不到
if ("true".equals(name)) {
return getDefaultExtension();
}
// 如果之前不存在这个名称对应的 Holder 对象,此处会创建一个空白的 Holder
// 调用 get() 方法会获得空对象
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建对象实例,并装进 holder 中
instance = createExtension(name);
holder.set(instance);
}
}
}
// 返回实例
return (T) instance;
}
2 createExtension
// org.apache.dubbo.common.extension.ExtensionLoader
private T createExtension(String name) {
// 如果 class 不存在就报错
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 尝试获取 class 对应的实例,如果不存在就创建
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 进行依赖注入
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);
}
}
##五点唠叨
1 Summary
The code of spi is a bit convoluted, so let's summarize it.
ExtensionLoader
ExtensionLoader is the facade of the entire spi system and an instance combination class of spi.
Built-in objects
EXTENSION_LOADERS
records the correspondence between class and loader
EXTENSION_INSTANCES
records the correspondence between class and instance objects. It can be thought of as a static global bean container.
Main method
getExtensionLoader(Class)
tries to create the loader object corresponding to this class;
But before creating the loader object of the general spi interface, the loader object of ExtensionFactory will be created first.
getExtension(String)
instantiates the object as required, places it in the EXTENSION_INSTANCES object, and then performs dependency injection and returns it to the user.
ExtensionFactory
Used as a factory for dependency injection.
ExtensionFactory is also a special spi class managed by ExtensionLoader.
2 not completed
Another very important part of dubbo's spi is the use of @Adaptive annotations. This part involves dynamic agents and is more complicated, so I have time to open a new chapter.