Dubbo之SPI机制

SPI

定义

spi(service provider interface)服务提供接口,它是通过约定一种接口实现的规约,来完成接口服务的扩展实现。

它是JDK内置的一种服务提供发现机制,目前市面上有很多框架都是用它来做服务的扩展发现,大家耳熟能详的如jdbc、日志框架都有用到,简单来说,它是一种动态替换发现的机制,举个例子,如果我们定义了一个规范,需要第三方厂商去实现,那么对于我们应用来说,只需要继承对应的厂商插件,既可以完成对应的实现机制。形成一种可插拔式的扩展手段

JDK实现SPI

JDK使用ServiceLoader实现spi的核心功能,

实现原理为

1、从MEDA_INF/services 目录中获取所有的类的配置文件

2、文件名必须是类的全路径包含包名

3、文件中输入所有的实现类

4、文件的格式必须是utf-8

java实现的spi有如下缺点

(1)spi会加载所有的扩展点,不管是否用到,这种记载方式,造成加载不灵活,浪费资源失败,很难进行定位问题

(2)如果spi加载

Dubbo实现SPI

dubbo修正了这样的缺点,并进行了改进,

dubbo实现中,大量使用了这种功能,所以想学习dubbo源码。必须学好spi,同时在dubbo的实现中也大量使用了缓存,就是内存缓存,在获取所有的对象之前都会先判断是否有相应的缓存,从这个方面可以看出,dubbo是基础单例模式实现的

解析的两种方式

Protocol protocol = ExtensionLoader. getExtensionLoader(Protocol.class). getExtension("myProtocol");

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class). getAdaptiveExtension();

getAdaptiveExtension源码解析

首先针对第二个进行源码分析,看一下它是如何获取一个动态扩展点的实例的

测试以Protocol为例

执行分为两个部分,首先是调用了 ExtensionLoader.getExtensionLoader(Protocol.class),那我们先从这里出发,一步步揭开它的神秘面纱

getExtensionLoader解析

//判断是否为空
if (type == null)
    throw new IllegalArgumentException("Extension type == null");
//判断该类型是否接口
if(!type.isInterface()) {
    throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//判断是否有SPI扩展点的备注@SPI
if(!withExtensionAnnotation(type)) {
    throw new IllegalArgumentException("Extension type(" + type + 
            ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//首先从缓存中查询是否已经有该类型的扩展器,如果有则取出
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;

继续看下 创建一个新的扩展器,它的构造方法里做了些什么

//保存Protocol.class对象
this.type = type;
//构建一个objectFactory对象,字面意思就是一个对象工厂 
//它的定义是 private final ExtensionFactory objectFactory;
objectFactory = (type == ExtensionFactory.class ? null :
        ExtensionLoader.getExtensionLoader(ExtensionFactory.class).
                getAdaptiveExtension());

暂时在这里不进行展开,在讲完上面的Protocol扩展器之后,在回看此方法,应该就能看懂了

书接上文,讲到就此构建了扩展器对象,这时候ExtensionLoader. getExtensionLoader(Protocol.class)代表的就是ExtensionLoader的实例对象了。

那么接下来我们继续解析后面的内容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

这里面一句话 做了很多的事情,通过嵌套方法调用,首先通过getAdaptiveExtensionClass()获取一个适配的class对象,接着对这个对象进行构建一个实例的操作,最后通过injectExtension方法,对该实例中所有set开头的方法进行依赖注入操作。

//可以实现扩展点的注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());

那么继续往下挖吧 >>getAdaptiveExtensionClass,就是你了,不要躲闪,你已经发光了

//获取一个扩展class对象
getExtensionClasses();
//TODO 判断是否缓存中存在该适配class对象,如果有则直接发回
if (cachedAdaptiveClass != null) {
    return cachedAdaptiveClass; //AdaptiveCompiler
}
//这里就是创建一个新的适配器对象了
return cachedAdaptiveClass = createAdaptiveExtensionClass();
​

都到这份上了 继续吧,迫不及待的想去征服它,,,嘎嘎getExtensionClasses 我来了

createAdaptiveExtensionClass

//不出所料的 还是从缓存从获取
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
    synchronized (cachedClasses) {
        classes = cachedClasses.get();
        if (classes == null) {
            //同样经历了双重检查锁之后,没有得到想要的结果,然后愤然自立更生,加载属于自己的扩展点,
            //该我表现的时刻终于来临了,come on
            classes = loadExtensionClasses();
            cachedClasses.set(classes);
        }
    }
}
return classes;

一条路走到黑,看它还有什么幺蛾子,loadExtensionClasses 来吧 让我在来看你一眼,好像某歌词,

蛾子来了,看来还是比较大啊,比较之前,代码已经增多不少,简单一看,发现了3个load操作,那么跟进吧

loadExtensionClasses

//type->Protocol.class
//得到SPI的注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) { //如果不等于空.
    String value = defaultAnnotation.value();
    if(value != null && (value = value.trim()).length() > 0) {
        String[] names = NAME_SEPARATOR.split(value);
        if(names.length > 1) {
            throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                    + ": " + Arrays.toString(names));
        }
        if(names.length == 1) cachedDefaultName = names[0];
    }
}
​
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;

本来不想把源码都贴进来的,后来发现,如果以小片段来分解 会有种 只有点没有面的尴尬,还是把代码都整理进来,这样就不用针对源码在对一遍了,因为亲,你看的都是原封不动的源码啊,

里面写的非常亲民,发现好多都是我们日常中很常见的,我初看这个源码的时候,错愕了一阵,这就是源码么,怎么没有其它框架的特质呢(ps:看不懂,懵圈的感觉没有出现),很是诧异,看来国产的还是走的爱国路线啊,必须赞一个,

代码的核心就是读取配置文件中所有的扩展点类,首先判断该扩展点类上是否有@Adaptive的注册,因为有这种注解的类,就是命中注定的不平凡呐,一注解注一生啊!它的伟大作用就是指定了它作为扩展器对象,无他,就此一个,其次就是判断该类中是否存在一个 clazz.getConstructor(type)这样的构造方法,如果存在则放入缓存,这个是wrapper类型的类,对具体的扩展对象 进行包装操作,之后进行一个扩展的功能处理,那么不是这两种呢,还是存到一个缓存中以备后用啊,在异常里我们看到了其他扩展点的身影,备胎的滋味,不好受啊,,,

走到这一步已经获取到需要的东东了,扩展点class已经确定了,或者有,或者需要创建动态可适配的,都有了定论

loadFile

String fileName = dir + type.getName();
try {
    Enumeration<java.net.URL> urls;
    ClassLoader classLoader = findClassLoader();
    if (classLoader != null) {
        urls = classLoader.getResources(fileName);
    } else {
        urls = ClassLoader.getSystemResources(fileName);
    }
    if (urls != null) {
        while (urls.hasMoreElements()) {
            java.net.URL url = urls.nextElement();
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                try {
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        final int ci = line.indexOf('#');
                        if (ci >= 0) line = line.substring(0, ci);
                        line = line.trim();
                        if (line.length() > 0) {
                            try {
                                String name = null;
                                int i = line.indexOf('=');
                                if (i > 0) {//文件采用name=value方式,通过i进行分割
                                    name = line.substring(0, i).trim();
                                    line = line.substring(i + 1).trim();
                                }
                                if (line.length() > 0) {
                                    Class<?> clazz = Class.forName(line, true, classLoader);
                                    //加载对应的实现类,并且判断实现类必须是当前的加载的扩展点的实现
                                    if (! type.isAssignableFrom(clazz)) {
                                        throw new IllegalStateException("Error when load extension class(interface: " +
                                                type + ", class line: " + clazz.getName() + "), class " 
                                                + clazz.getName() + "is not subtype of interface.");
                                    }
​
                                    //判断是否有自定义适配类,如果有,则在前面讲过的获取适配类的时候,直接返回当前的自定义适配类,不需要再动态创建
                                    if (clazz.isAnnotationPresent(Adaptive.class)) {
                                        if(cachedAdaptiveClass == null) {
                                            cachedAdaptiveClass = clazz;
                                        } else if (! cachedAdaptiveClass.equals(clazz)) {
                                            throw new IllegalStateException("More than 1 adaptive class found: "
                                                    + cachedAdaptiveClass.getClass().getName()
                                                    + ", " + clazz.getClass().getName());
                                        }
                                    } else {
                                        try {
                                            //如果没有Adaptive注解,则判断当前类是否带有参数是type类型的构造函数,如果有,则认为是
                                            //wrapper类。这个wrapper实际上就是对扩展类进行装饰.
                                            //可以在dubbo-rpc-api/internal下找到Protocol文件,发现Protocol配置了3个装饰
                                            //分别是,filter/listener/mock. 所以Protocol这个实例来说,会增加对应的装饰器
                                            clazz.getConstructor(type);//
                                            //得到带有public DubboProtocol(Protocol protocol)的扩展点。进行包装
                                            Set<Class<?>> wrappers = cachedWrapperClasses;
                                            if (wrappers == null) {
                                                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                wrappers = cachedWrapperClasses;
                                            }
                                            wrappers.add(clazz);//包装类 ProtocolFilterWrapper(ProtocolListenerWrapper(Protocol))
                                        } catch (NoSuchMethodException e) {
                                            clazz.getConstructor();
                                            if (name == null || name.length() == 0) {
                                                name = findAnnotationName(clazz);
                                                if (name == null || name.length() == 0) {
                                                    if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                            && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                        name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                    } else {
                                                        throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                    }
                                                }
                                            }
                                            String[] names = NAME_SEPARATOR.split(name);
                                            if (names != null && names.length > 0) {
                                                Activate activate = clazz.getAnnotation(Activate.class);
                                                if (activate != null) {
                                                    cachedActivates.put(names[0], activate);
                                                }
                                                for (String n : names) {
                                                    if (! cachedNames.containsKey(clazz)) {
                                                        cachedNames.put(clazz, n);
                                                    }
                                                    Class<?> c = extensionClasses.get(n);
                                                    if (c == null) {
                                                        extensionClasses.put(n, clazz);
                                                    } else if (c != clazz) {
                                                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            } catch (Throwable t) {
                                IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                exceptions.put(line, e);
                            }
                        }
                    } // end of while read lines
                } finally {
                    reader.close();
                }
            } catch (Throwable t) {
                logger.error("Exception when load extension class(interface: " +
                                    type + ", class file: " + url + ") in " + url, t);
            }
        } // end of while urls
    }
} catch (Throwable t) {
    logger.error("Exception when load extension class(interface: " +
            type + ", description file: " + fileName + ").", t);
}

接下来回到初始的调用点getExtensionClasses方法我们已经走完流程了,那么继续往下走,查看缓存中是否存在适配点,如果没有则需要进行动态生成一个了,有的话不用说了 直接返回,如果没有则走该方法,进行动态生成一个适配class

createAdaptiveExtensionClass

getExtensionClasses();
//TODO  不一定?
if (cachedAdaptiveClass != null) {
    return cachedAdaptiveClass; //AdaptiveCompiler
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();

来来来,让我们看一下,通过下面的代码生成并且完成了扩展点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);

上面生成的类的源码有必要展示一下,这样可以明白它到底是如何实现的适配的

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.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.P
rotocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
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.P
rotocol.class).getExtension(extName);
return extension.export(arg0);
}
}

createAdaptiveExtensionClassCode方法具体的创建过程就不贴代码了,并不影响对SPI的理解,到这一步就创建完成了一个适配点

PS 之前的objectFactory 我们现在可以讲一下了,通过上文你应该能够自己去试着理解了,它同样是创建了一个适配点对象,在配置文件中我们发现它的实现类AdaptiveExtensionFactory类上面有注解@Adaptive 于是乎,聪明的你应该能想到是怎么回事了,对,创建的就是这个对象,上面已经交代清楚了,如果还是不清楚,请回看,看仔细 看明白 ,

getExtension("myProtocol")源码解析

上面讲解的是获取动态的适配器扩展点,这个就是获取指定的扩展点,参数里面传递着具体的扩展点名称,好吧,我们开始吧

开始还是一通判断上面已经有讲解,不在重复了

getExtension("myProtocol")

if (name == null || name.length() == 0)
    throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
    return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
    cachedInstances.putIfAbsent(name, new Holder<Object>());
    holder = cachedInstances.get(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;

这里面的的第一句看到了熟悉的方法的身影getExtensionClasses,它不就是上面讲的去加载扩展类的么,这里亦不在重复,接下来就是创建该类型的对象了,通过反射,都知道的云云,,,

然后还是有injectExtension依赖注入这一块,就是注入有set方法的,在往下有一个重点就是注入wrapper对象进行扩展点的包装,形成a>b>dubbo这种形式,最后把该扩展点对象进行返回,就完成了指定扩展点对象的获取

createExtension

猜你喜欢

转载自blog.csdn.net/hezhaomeng/article/details/82621112