dubbo的spi机制和jdk机制区别
原理类似,都是从指定文件中加载实现类,但是JDK的加载逻辑比较简单
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源
- 如果扩展点加载失败,JDK SPI 没给出详细信息,不方便定位问题,Dubbo SPI 在失败时记录真正的失败原因,并打印出来
- 增加IOC和AOP的能力
- 增加排序能力
- 增加条件激活能力提供了一系列更灵活的 API,如获取所有 SPI 扩展实现、根据名称查询某个扩展实现、根据类型查询扩展实现、查询匹配条件的扩展实现等
从源码了解Dubbo的SPI机制
Dubbo 中,SPI 加载固定扩展类的入口是 ExtensionLoader 的 getExtension 方法
public T getExtension(String name, boolean wrap) {
checkDestroyed();
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 获取默认扩展类
// 在注解@SPI可以设置value值,为对应实现类的名字,并且在getExtension中设置true值,则获取默认设置的值
if ("true".equals(name)) {
return getDefaultExtension();
}
String cacheKey = name;
if (!wrap) {
cacheKey += "_origin";
}
final Holder<Object> holder = getOrCreateHolder(cacheKey);
Object instance = holder.get();
//如果有两个线程同时来获取同一个name的扩展点对象,那只会有一个线程会进行创建
// 双重锁,双重检查
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建扩展点实例对象
instance = createExtension(name, wrap);//创建扩展点对象
holder.set(instance);
}
}
}
return (T) instance;
}
创建扩展点实例对象,dubbo在创建扩展点对象的时候,实现了简单的IOC和AOP逻辑,
private T createExtension(String name, boolean wrap) {
// 获取扩展类{name: Class} 接口的所有实现类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
// ioc 实例缓存
T instance = (T) extensionInstances.get(clazz);
if (instance == null) {
// 创建实例,放到缓存
extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
instance = (T) extensionInstances.get(clazz);
instance = postProcessBeforeInitialization(instance, name);
// 依赖注入
injectExtension(instance);
instance = postProcessAfterInitialization(instance, name);
}
// aop
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
// 进行排序
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
// 遍历包装类
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
boolean match = (wrapper == null) ||
((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
!ArrayUtils.contains(wrapper.mismatches(), name));
if (match) {
// 获取对饮过构造方法是参数是这个类型的wrapper,然后属性注入injectExtension
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
instance = postProcessAfterInitialization(instance, name);
}
}
}
}
// Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
ioc逻辑中,先从缓存获取,如果获取不到,尝试实例化对象,并通过setter方法初始化对象的成员变量。目前dubbo仅支持setter方法注入
首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
// 遍历目标类的所有方法
for (Method method : instance.getClass().getMethods()) {
// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
if (!isSetter(method)) {
continue;
}
/**
* 检测是否有 DisableInject 注解修饰.
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
/**
* 检测是否实现了ScopeModelAware、ExtensionAccessorAware类,如果实现则不注入
*/
if (method.getDeclaringClass() == ScopeModelAware.class) {
continue;
}
if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
continue;
}
}
// 基本类型不注入
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取属性名,比如 setName 方法对应属性名 name
String property = getSetterProperty(method);
// 从 ObjectFactory 中获取依赖对象
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;
}
在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展
aop机制
Dubbo AOP 机制采用 wrapper 设计模式实现,要成为一个 AOP wrapper 类,必须同时满足以下几个条件:
- wrapper 类必须实现 SPI 接口,如
class QosProtocolWrapper implements Protocol - 构造器 constructor 必须包含一个相同的 SPI 参数,如
QosProtocolWrapper(Protocol protocol) - wrapper 类必须和普通的 SPI 实现一样写入配置文件,如
resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
wrapper示例如下:
public class QosProtocolWrapper implements Protocol, ScopeModelAware {
private final Protocol protocol;
public QosProtocolWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
}
写入配置文件 resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol:
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
在通过 getExtension(name) 尝试获取并加载 SPI 扩展实例时,Dubbo 框架会判断所有满足以上 3 个条件的 wrapper 类实现,并将 wrapper 类按顺序包在实例外面,从而达到 AOP 拦截的效果。
以下是 wrapper 判断与加载的实现逻辑,你还可以使用 @Wrapper 注解来控制 wrapper 类的激活条件
private T createExtension(String name, boolean wrap) {
Class<?> clazz = getExtensionClasses().get(name);
T instance = (T) extensionInstances.get(clazz);
// ...
if (wrap) { // 如果调用方告知需要 AOP,即 wrap=true
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
// 通过 @Wrapper 注解判断当前 wrapper 类是否要生效
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
boolean match = (wrapper == null)
|| ((ArrayUtils.isEmpty(wrapper.matches())
|| ArrayUtils.contains(wrapper.matches(), name))
&& !ArrayUtils.contains(wrapper.mismatches(), name));
if (match) {
instance = injectExtension(
(T) wrapperClass.getConstructor(type).newInstance(instance));
instance = postProcessAfterInitialization(instance, name);
}
}
}
}
}