Dubbo SPI扩展和自适应扩展

·  阅读 314

这篇文章会分文两部分: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在是使用方面,跟上述例子有些不同:

  1. 首先要在Root接口上加上@SPI注解

  2. 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生成的类。ProtocolAdaptiveProxyFactoryAdaptive和ProxyFactoryAdaptive本质就是代理类。

我们重点看看这行:

 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加载扩展类,由扩展类执行真正的逻辑。

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改