Dubbo SPI是什么
Dubbo SPI(Service Provider Interface),用于根据名称获取Interface接口的实现类,根据条件激活Interface接口的实现类集合,即一组实现类。并且是根据JAVA SPI扩展而来,具有如下特性:
- 按需要加载Interface接口的实现类,JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实,现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
Dubbo SPI获取扩展实现类的大致入流如下:
在激活实现类集合时,也是根据SPI机制逐个获取实现类,所以我们重点分析如何根据SPI机制获取扩展实现类即可。
Dubbo SPI如何实现
从配置文件获取扩展实现类配置
根据名称获取扩展实现类时,首先需要从配置文件加载名称与扩展实现类的映射配置,从不同位置的文件加载。实例,以加载dubbo中自定义实现的线程池ThreadPool为例,在dubbo中,定义ThreadPool线程池的接口,在基于dubbo SPI的机制中,可以根据name为fixed,cached,limited,eager分别获取到对应的实现类FixedThreadPool,CachedThreadPool,LimitedThreadPool,EagerThreadPool。线程池的其具体实现可以参考为什么要使用线程池?dubbo是如何扩展的?,其SPI文件配置如图:
配置文件以Interface全限定名称为文件名,配置内容以name=实现类的形式配置,其源码如下:
fixed=org.apache.dubbo.common.threadpool.support.fixed.FixedThreadPool
cached=org.apache.dubbo.common.threadpool.support.cached.CachedThreadPool
limited=org.apache.dubbo.common.threadpool.support.limited.LimitedThreadPool
eager=org.apache.dubbo.common.threadpool.support.eager.EagerThreadPool
ExtensionLoader调用getExtension(String name)获取扩展实现类,初次加载会调用getExtensionClasses从配置文件获取扩展实现类的Class与名称的配置。其源码如下:
// 根据name获取扩展实现类
// ExtensionLoader.class
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(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;
}
// 根据name生成扩展实现类
// ExtensionLoader.class
private T createExtension(String name) {
// getExtensionClasses 获取扩展实现类的Class配置
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 通过反射,使用Class生成实例对象
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) {
// 实例化wrapper对象
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
// IOC机制使用setter方式注入属性实例对象
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
// 从配置文件获取扩展实现类的Class与名称配置
// ExtensionLoader.class
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// 加载扩展实现配置
// ExtensionLoader.class
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
// 从指定文件路径加载
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
dubbo定义LoadingStrategy加载策略接口,在不同文件路径的配置文件,其实现类如下:
- DubboExternalLoadingStrategy,扩展加载策略,从”META-INF/dubbo/external/”文件路径加载。
- DubboInternalLoadingStrategy,内部加载策略,从”META-INF/dubbo/internal/”文件路径加载。
- DubboLoadingStrategy,用户自定义加载策略,从”META-INF/dubbo/”文件路径加载。
- ServicesLoadingStrategy,JAVA SPI加载策略,从”META-INF/services/”文件路径加载。
扩展实现类的初始化
获取到扩展实现类的配置时,dubbo使用Class对象,用反射机制生成具体实现类。如果,是使用装饰模式的wraper装饰类,则使用带有指定接口类型的构造函数生成实现类。如果,扩展实现类包含其他实现类的属性,则使用IOC机制,基于setter属性设置的机制设置属性。其源码如下:
// 根据name生成扩展现类实例
// ExtensionLoader.class
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 使用Class的默认构造函数反射生成实例对象
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// IOC机制,setter方式设置属性值
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
// 如果是wrapper装饰类,使用Class的指定type构造函数反射生成实例对象
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);
}
}
// 调用set方法设置objectFactory生成的属性值
// ExtensionLoader.class
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
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;
}
一般dubbo中,ExtensionLoader初始化时,会根据@Adaptive使用javassist动态生成ExtensionFactory代理类。该ExtensionFactory代理类,能够根据URL传递的name参数动态获取指定的实现类。具体的自适应机制可以参考官网地址自适应扩展机制原理,其部分源码如下:
// ExtensionLoader.class
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<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
初始化ExtensionLoader
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
// 初始化ExtensionLoader
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null :
// 根据Adaptive注解标识,使用javsssit动态代理生成具体的代理类
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
我们从中能够学习到什么
- IOC:类似于spring的属性注入简化版,能够根据自适应扩展机制,动态的根据URL参数设置属性值。
- 动态代理,类似AOP,通过@Adaptive注解,标识代理的方法,使用Javassit动态编译代理逻辑。在dubbo中,实现能够根据URL参数获取指定name,然后通过name获取对应的扩展实现类。具体实例可以参考代理(Proxy)是什么?为什么要使用代理模式?Spring与Dubbo如何运用代理模式的?
- 反射,在读取扩展实现的配置后,适应累加器读转换为JVM中的Class对象,通过反射机制创建无参数的或者装饰模式的实例对象。
- 装饰模式,具体实现可以参考装饰器模式-Mybatis教你如何玩。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13611.html