这篇文章会分文两部分:Dubbo SPI扩展和自适应扩展
Dubbo SPI扩展
首先来看一个例子,来搞懂什么是SPI扩展,这个例子官网也有,自己敲一下,加深印象
// 定一个Root接口
public interface Root {
// 这里只有一个方法
void sayHello();
}
实现类
public class SpringRoot implements Root {
@Override
public void sayHello() {
System.out.println("SpringRoot...");
}
}
public class DubboRoot implements Root {
@Override
public void sayHello() {
System.out.println("DubboRoot...");
}
}
复制代码
然后在META-INF下,创建services目录,在目录下,再创建一个文件名为dubbo.learn.basic.Root,就是Root接口的全限定名,文件里的内容如下:
dubbo.learn.basic.SpringRoot
dubbo.learn.basic.DubboRoot
复制代码
最后,写个启动类
public class RootTest {
public static void main(String[] args) {
ServiceLoader<Root> serviceLoader = ServiceLoader.load(Root.class);
serviceLoader.forEach(Root::sayHello);
}
}
复制代码
打印的结果是:
SpringRoot...
DubboRoot...
这里用到的是jdk的SPI机制,ServiceLoader把所有的实现类都加载进来了,而这个并不是Dubbo想要的,Dubbo是按需加载,所以Dubbo自己实现了一套SPI机制。
Dubbo SPI在是使用方面,跟上述例子有些不同:
-
首先要在Root接口上加上@SPI注解
-
dubbo.learn.basic.Root文件的内容形式为key-value,如springRoot= com.hzed.dubbo.learn.basic. SpringRoot
然后我们在改一下启动类
public class RootTest {
public static void main(String[] args) {
ExtensionLoader<Root> extensionLoader = ExtensionLoader.getExtensionLoader(Root.class);
Root springRoot = extensionLoader.getExtension("springRoot");
springRoot.sayHello();
}
}
复制代码
打印的结果是:
SpringRoot...
Dubbo SPI核心的是ExtensionLoader,所以我们先来分析一下ExtensionLoader
getExtensionLoader
首先是创建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 interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// 首先从缓存获取,如果缓存没有,就直接new
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;
}
复制代码
ExtensionLoader的构造方法是私有的,所以我们不能直接new,而是通过静态方法来获取对象,接下来看一下ExtensionLoader的构造方法
private ExtensionLoader(Class<?> type) {
this.type = type;
// 这里会调两次,第一次是Root.class ; 第二次是ExtensionFactory.class;但是在我们的demo里,objectFactory为空,为什么会为空?后面讲自适应扩展时再分析
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
复制代码
构造方法其实很简单,在这里,其实可以忽略objectFactory,所以当前对象的type,是Root.class。接下来,继续看看getExtension方法
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
// 目标对象会封装成holder
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
// 双重校验,在Dubbo里经常看得到
Object instance = holder.get();
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) {
// 首先是加载文件,然后根据指定的名字获取扩展类的Class,关于加载和解析文件,官网有详细的解析 https://dubbo.apache.org/zh/docs/v2.7/dev/source/dubbo-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);
}
// 依赖注入
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
复制代码
整个思路也是清晰的,这里要提一下,我们看源码一开始不要扣细节,而是从整理上理解流程,做到由体到面,由面到点,只要细节不妨碍我们的理解,完全可以跳过,比如getExtensionClasses(),这里对我们理解这个方法并不影响,所以可以直接跳过。如果对这个感兴趣的,可以继续进去探索
这里有个injectExtension,是Dubbo的依赖注入
private T injectExtension(T instance) {
try {
// objectFactory是ExtensionFactory,它可能是Spring ApplicationContext和Dubbo的SpiExtensionFactory、AdaptiveExtensionFactory,通过这些容器寻找依赖的对象
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
/**
* 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];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// Dubbo通过set方法,把依赖对象object注入到instance
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
复制代码
到此,Dubbo SPI整体框架基本出来了
SPI 自适应扩展
什么是自适应?官网的描述是这样的:
有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。
然后官网还给了WheelMaker的例子。(可能看完后还是不知道什么是自适应)
有句话,我们要抓住以下,就是“在拓展方法被调用时,根据运行时参数进行加载”,也就是说,我们使用扩展类,是在调用接口方法时来决定使用哪个扩展类作为接口的实现类的,这里举例子说明
看一下ServiceConfig
public class ServiceConfig<T> extends AbstractServiceConfig {
//...
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//...
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...
Exporter<?> exporter = protocol.export(wrapperInvoker);
...
}
//...
}
复制代码
doExportUrlsFor1Protocol方法是服务导出时被调的,关于服务导出,等后面文章再写。我们平时指定协议时,指定协议名称,如<dubbo:protocol name="dubbo" port="2880" />,dubbo就是我们要是用的扩展类,假如我自己新增了扩展类,那么就可以指定我自己的扩展类。
也就是说,代码知道走到protocol.export(wrapperInvoker)这行,才知道具体要使用的扩展类是DubboProtocol。这里就好奇了,代码一开始就给protocol赋值了,那赋值的类是什么类?带着这个问题,开始分析自适应扩展框架。
而关键的入口,就是上面的 Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class) . getAdaptiveExtension()
因为getExtensionLoader会创建ExtensionLoader对象,所以我们再看一下构造方法
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
复制代码
前面说过,这个构造方法会被调用两次,那是因为第二次是执行了ExtensionLoader.getExtensionLoader ( ExtensionFactory.class) ,当执行到这里是,type是ExtensionFactory.class,objectFactory是null,也就是说,首先要去加载ExtensionFactory的扩展类
以下的代码分析,是基于要加载ExtensionFactory的扩展类
getAdaptiveExtension
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 其他没啥好说,这里是创建扩展类
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
复制代码
createAdaptiveExtension
private T createAdaptiveExtension() {
try {
// 依赖注入不是重点,直接看getAdaptiveExtensionClass
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
复制代码
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 也是通过文件加载扩展类
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
// 这个方法很长,细节很多,建议直接看官网,细节不是我们这次掌握的目的,
// 但是返回的结果确实我们所关心的
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
复制代码
当代码执行getExtensionClasses()时,ExtensionFactory的实现类AdaptiveExtensionFactory,是一个被注解@Adaptive标注的,方法里面有个分支判断,如果被这个注解标注的类,就设置为cachedAdaptiveClass,所以此处的cachedAdaptiveClass不为空,直接返回了cachedAdaptiveClass,也就是AdaptiveExtensionFactory。
此时,方法getAdaptiveExtensionClass()返回cachedAdaptiveClass,然后通过getAdaptiveExtensionClass() .newInstance()创建对象,再完成依赖注入。
到此,objectFactory完成赋值
AdaptiveExtensionFactory
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@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;
}
}
复制代码
从AdaptiveExtensionFactory类可以看到,构造方法把ExtensionFactory添加到list,getSupportedExtensions其实又是调用getExtensionClasses(),或者ExtensionFactory的所有扩展类
getExtension方法直接调用ExtensionFactory的getExtension方法,我们看看具体的实现SpiExtensionFactory
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
复制代码
SpiExtensionFactory其实也没做什么,又是调用ExtensionLoader的getAdaptiveExtension()
createAdaptiveExtensionClass
这里可能会有点绕,我们整理一下:
回到ServiceConfig:当private static final Protocol protocol = ExtensionLoader.getExtensionLoader (Protocol.class). getAdaptiveExtension();
当执行ExtensionLoader.getExtensionLoader(Protocol.class),方法进入构造器ExtensionLoader(Class< ?> type),此时type = Protocol.class,当执行构造器的objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())时,再次进入构造器时,此时type = ExtensionFactory.class,这很好理解,普通的扩展类需要创建,ExtensionFacto-ry 也需要创建,但需要注意,是两个类的构造器
当第二个构造器返回后,代码会走到第一个构造器那里,第一个构造器返回后,代码会继续执行getAdaptiveExtens- ion()
前面已经分析过getAdaptiveExtension了,这里就不重复了,这里要讲的是createAdaptiveExtensionClass(), 这里再贴一下代码
private Class<?> createAdaptiveExtensionClass() {
// 这个方法很长,细节很多,建议直接看官网,细节不是我们这次掌握的目的,
// 但是返回的结果确实我们所关心的
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
复制代码
createAdaptiveExtensionClassCode() 是创建扩展类的,我们不看里面的细节,而是看它返回了什么
如果以Protocol为例,就返回:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException(
"method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException(
"method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(
com.alibaba.dubbo.rpc.Invoker arg0)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
com.alibaba.dubbo.common.URL arg1)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
com.alibaba.dubbo.common.URL url = arg1;
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.refer(arg0, arg1);
}
}
复制代码
如果以ProxyFactory为例,就返回:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0,
java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg2 == null) {
throw new IllegalArgumentException("url == null");
}
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" +
url.toString() + ") use keys([proxy])");
}
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class)
.getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0,
boolean arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" +
url.toString() + ") use keys([proxy])");
}
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class)
.getExtension(extName);
return extension.getProxy(arg0, arg1);
}
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" +
url.toString() + ") use keys([proxy])");
}
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class)
.getExtension(extName);
return extension.getProxy(arg0);
}
}
复制代码
我们对比看看两个类有什么相同之处,细心的人可能会发现,方法的逻辑是一样的,不同的地方是变量和变量类型,这些都是用字符串拼接,然后通过javassist生成的类。ProtocolAdaptive本质就是代理类。
我们重点看看这行:
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
复制代码
然后再结合开头引用的官网描述“在拓展方法被调用时,根据运行时参数进行加载”,不知是否已经理解了“自适应”的意思了。
前面提出的问题,已经有答案:Protocol protocol是Protocol$Adaptive实例,当调用protocol.export()时,由代理类的再去加载具体实现类,然后再调用实现类的export(),这整个流程就是SPI自适应扩展。
总结
这边文章我们介绍了SPI基本原理和Dubbo SPI和自适应扩展,从始至终我们关注的是整体框架,至于一些细节,这里并没有介绍,因为我觉得细节要么可以看代码,一边看一遍debug,要么看看官网。
Dubbo SPI自适应扩展也没有多玄乎,一开始看就可能觉得有点绕而已,其实本质就是,在运行时生成各个接口的代理类,代理类,在通过Dubbo SPI加载扩展类,由扩展类执行真正的逻辑。