2019独角兽企业重金招聘Python工程师标准>>> 
dubbo的SPI机制
关于dubbo的SPI机制请参阅dubbo开发者文档 -> http://dubbo.io/Developer+Guide-zh.htm#DeveloperGuide-zh-%E6%89%A9%E5%B1%95%E7%82%B9%E5%8A%A0%E8%BD%BD
我只把我自己理解的SPI与大家分享一下:
SPI是面向dubbo开发者角色的扩展接口,开发者可以通过它来扩展dubbo的功能和技术实现。相对而言API是面向dubbo使用者角色编程接口。
SPI解决的是扩展内容配置和动态加载的问题。在java中解决相同或者类似问题的技术有OSGI,JDK自带的SPI,以及IOC框架Spring也能够解决类似的问题,各种解决方案各有特点,我们不展开讲。而dubbo的SPI是从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来,它做了如下改进:(这些内容引用自dubbo官方开发者手册)
- JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK标准的ScriptEngine,通过getName();获取脚本类型的名称,但如果RubyScriptEngine因为所依赖的jruby.jar不存在,导致RubyScriptEngine类加载失败,这个失败原因被吃掉了,和ruby对应不起来,当用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因。
- 增加了对扩展点IoC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。
那么dubbo的SPI是如何实现的呢?让我们直接看源码一探究竟,SPI的源码位于工程dubbo-common的包com.alibaba.dubbo.common.extension下。
dubbo的SPI源码实现分析
ExtensionLoader
该类是dubbo的SPI机制实现的最为核心的一个类,绝大多数实现逻辑均位于该类中,因此我们从该类入手研究。为了简化,该类是一个将使用api及核心逻辑实现都封装同一个类中。通常获得一个扩展接口的实例使用如下接口方法获得。
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME);
静态工厂方法getExtensionLoader
该方法是一个静态工厂方法,是一个编程api,通过该方法获得一个某个参数制定的扩展类的ExtensionLoader对象。
private static boolean withExtensionAnnotation(Class type) {
return type.isAnnotationPresent(SPI.class);
}
@SuppressWarnings("unchecked")
public static ExtensionLoader getExtensionLoader(Class 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!");
}
ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
}
return loader;
}
从看源码我们得出几个结论。
- dubbo的SPI扩展点必须是接口。
- dubbo的SPI扩展点接口必须用注解SPI标注。
- 某个扩展点的ExtensionLoader是获取的时候延迟生成,并且会进行缓存。
获得扩展实例方法getExtension
/**
* 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
*
* @param name
* @return
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder