dubbo源码阅读之SPI
dubbo spi
spi,全程service provider interface, java中的一种借口扩展机制,将借口的实现类注明在配置文件中,程序在运行时通过扫描这些配置文件从而获取全部的实现类。
java 原生的spi将借口的实现类信息放在meta-inf/services/文件中。spi被广泛应用于各种框架及中间件中,用以提升框架的扩展性。
dubbo也使用spi来提供各种扩展,但是dubbo并未使用java原生的spi,而是实现了自己的spi机制。dubbo的spi实现extensionloader相较于java自带的serviceloader还是有不少改进的,例如为每个实现类赋一个名称,以k-v存储在配置文件中,并且在代码中也可以通过名称获取到相应的实现类,另外,extensionloader的加载目录也比serviceloader更多,
extensionloader.getextensionloader
这个方法是入口
public static <t> extensionloader<t> getextensionloader(class<t> type) { //做一些非空检查 if (type == null) { throw new illegalargumentexception("extension type == null"); } //必须是接口,spi是针对接口的扩展机制 if (!type.isinterface()) { throw new illegalargumentexception("extension type (" + type + ") is not an interface!"); } //必须带有spi注解 if (!withextensionannotation(type)) { throw new illegalargumentexception("extension type (" + type + ") is not an extension, because it is not annotated with @" + spi.class.getsimplename() + "!"); } //extension_loaders是extensionloader的全局缓存,interface->extensionloader 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.getextension(string name)
获取一个extensionloader实例后,通过该方法获取一个扩展类的实例
private t createextension(string name) { //核心逻辑。找到并加载所有的扩展类 class<?> clazz = getextensionclasses().get(name); if (clazz == null) { throw findexception(name); } try { //extension_instances是扩展类实例的全局缓存 t instance = (t) extension_instances.get(clazz); if (instance == null) { extension_instances.putifabsent(clazz, clazz.newinstance()); instance = (t) extension_instances.get(clazz); } //自动注入一些属性,实现ioc特性 injectextension(instance); //对原始实例进行包装,实现aop特性 set<class<?>> wrapperclasses = cachedwrapperclasses; if (collectionutils.isnotempty(wrapperclasses)) { //层层包装 //这里有个问题,如果包装类有多个,那么他们的顺序如何??? //spring中对于多个通知类advice的情况是会进行排序的 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 + ") couldn't be instantiated: " + t.getmessage(), t); } }
getextensionclasses
//加载该接口所有的扩展类,这个方法主要通过双检查锁机制实现了缓存逻辑, 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; }
loadextensionclasses
//这个方法返回的类中不包括带有adaptive注解的类,以及包装类 private map<string, class<?>> loadextensionclasses() { //缓存默认扩展类的名称 //默认扩展类名称通过接口上的spi注解获取,就是spi注解的value cachedefaultextensionname(); //将加载的扩展类实例放到该map中, map<string, class<?>> extensionclasses = new hashmap<>(); //加载 meta-inf/dubbo/internal/目录下的配置文件 loaddirectory(extensionclasses, dubbo_internal_directory, type.getname()); //加载 meta-inf/dubbo/internal/目录下alibaba自己的相关接口,通过将org.apache替换为com.alibaba loaddirectory(extensionclasses, dubbo_internal_directory, type.getname().replace("org.apache", "com.alibaba")); //加载 meta-inf/dubbo/目录下的配置文件 loaddirectory(extensionclasses, dubbo_directory, type.getname()); //同上 loaddirectory(extensionclasses, dubbo_directory, type.getname().replace("org.apache", "com.alibaba")); //加载 meta-inf/services/目录下的配置文件 //这里需要注意的是,由于项目中可能会引入使用jdk serviceloader的包, // 那么 meta-inf/services/目录下可能存在serviceloader的配置文件,而这些文件中存储的实现类并不是以key-value形式存储的 // 这样extensionloader在加载的时候就找不到name,这个后续会进行一些处理 loaddirectory(extensionclasses, services_directory, type.getname()); //同上 loaddirectory(extensionclasses, services_directory, type.getname().replace("org.apache", "com.alibaba")); return extensionclasses; }
可以看出加载了meta-inf/dubbo/internal/,meta-inf/dubbo/,meta-inf/services/三个目录下的配置文件,除了加载原接口相关的配置文件,extensionloader额外加载了alibaba自己的相关接口的扩展类。接下来,我们看一下loaddirectory方法。
loaddirectory
//从相关的目录找到对应接口类型的配置文件,并加载全部扩展类 private void loaddirectory(map<string, class<?>> extensionclasses, string dir, string type) { //配置文件全路径:文件夹+接口名称,meta string filename = dir + type; try { enumeration<java.net.url> urls; //首先获取classloader, 获取顺序是:线程上下文类加载器->加载extensionloader的类加载器->系统类加载器(appclassloader) classloader classloader = findclassloader(); if (classloader != null) { //获取资源文件的url urls = classloader.getresources(filename); } else { //多此一举?? urls = classloader.getsystemresources(filename); } if (urls != null) { //遍历找到的所有文件,依次加载进来 while (urls.hasmoreelements()) { java.net.url resourceurl = urls.nextelement(); //加载一个文件中定义的扩展类 loadresource(extensionclasses, classloader, resourceurl); } } } catch (throwable t) { logger.error("exception occurred when loading extension class (interface: " + type + ", description file: " + filename + ").", t); } }
loadresource
private void loadresource(map<string, class<?>> extensionclasses, classloader classloader, java.net.url resourceurl) { try { //配置文件必须是utf-8编码格式 try (bufferedreader reader = new bufferedreader(new inputstreamreader(resourceurl.openstream(), standardcharsets.utf_8))) { string line; 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; //文件中每一行以:name=extension.class.name格式存储扩展类, //以=号作为分隔符,就是properties文件格式 int i = line.indexof('='); //这里少考虑了i==0的情况???? //name可以是空,也就是说可以只有扩展类名,而没有name, //实际上这种格式就是serviceloader的资源文件, // 对于name为空的情况的处理逻辑在loadclass方法中 if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { //重点关注一下name为空的处理逻辑 //这里调用class.forname加载实现类 loadclass(extensionclasses, resourceurl, class.forname(line, true, classloader), name); } } catch (throwable t) { illegalstateexception e = new illegalstateexception("failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceurl + ", cause: " + t.getmessage(), t); //记录下加载失败的类,异常及错误信息 exceptions.put(line, e); } } } } } catch (throwable t) { logger.error("exception occurred when loading extension class (interface: " + type + ", class file: " + resourceurl + ") in " + resourceurl, t); } }
这里少考虑了i==0的情况????
loadclass
//加载一个扩展类 private void loadclass(map<string, class<?>> extensionclasses, java.net.url resourceurl, class<?> clazz, string name) throws nosuchmethodexception { //首先检查是不是相应接口的子类 if (!type.isassignablefrom(clazz)) { throw new illegalstateexception("error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getname() + "), class " + clazz.getname() + " is not subtype of interface."); } //设置adaptive, adaptive只能有一个,如果有多个扩展类上都有adaptive注解,那么会抛异常 if (clazz.isannotationpresent(adaptive.class)) { cacheadaptiveclass(clazz); //保存包装类, //判断包装类就是看这个类有没有只有一个参数的构造器,而且参数的类型必须是对应的扩展接口类型 } else if (iswrapperclass(clazz)) { cachewrapperclass(clazz); } else { //代码进入这个分支,说明该类是普通的扩展类 //必须要有无参构造器 clazz.getconstructor(); //前面也讲过,name是可以为空的,资源文件中可以只有实现类的类名称 if (stringutils.isempty(name)) { //通过extension注解找name的值, //如果没有extension注解,那么通过类名称获取name值,具体处理方法不细说 name = findannotationname(clazz); if (name.length() == 0) { //extension注解中的value为空字符串,这种情况抛异常 throw new illegalstateexception("no such extension name for the class " + clazz.getname() + " in the config " + resourceurl); } } //可以有多个别名,类似spring中bean的别名,多个名称以逗号分隔 string[] names = name_separator.split(name); if (arrayutils.isnotempty(names)) { //如果类带有activate注解,那么将其缓存下来 cacheactivateclass(clazz, names[0]); //将 for (string n : names) { //将名字缓存下来,只缓存第一个名字 cachename(clazz, n); //将加载的类放到一路传进来的extensionclasses中, //如果有多个别名,每个别名都存储 saveinextensionclass(extensionclasses, clazz, name); } } } }
至此,就查找到了该接口的全部扩展类。
实现ioc特性
前面讲到,在createextension方法中,创建完实例后,会调用injectextension方法自动组注入一些属性,
injectextension
private t injectextension(t instance) { try { //objectfactory是adaptiveextensionfactory的一个实例 if (objectfactory != null) { for (method method : instance.getclass().getmethods()) { if (issetter(method)) { /** * 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); 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; }
我们首先看一下objectfactory成员是怎么来的
private extensionloader(class<?> type) { this.type = type; objectfactory = (type == extensionfactory.class ? null : extensionloader.getextensionloader(extensionfactory.class).getadaptiveextension()); }
对于一般的接口类型,objectfactory成员就是带adaptive注解的extensionfactory接口的实现类,其实就是adaptiveextensionfactory的一个实例。
adaptiveextensionfactory
//该类与dubbo spi的自适应机制相关 @adaptive public class adaptiveextensionfactory implements extensionfactory { private final list<extensionfactory> factories; public adaptiveextensionfactory() { //加载extensionfactory接口的所有实现类 //并缓存到factories中 extensionloader<extensionfactory> loader = extensionloader.getextensionloader(extensionfactory.class); list<extensionfactory> list = new arraylist<extensionfactory>(); //这里思考:为什么adaptiveextensionfactory不会循环调用构造器??? //原因在extensionloader.loadclass方法中,adaptive注解的类和包装类都只缓存下来,不在正常的查找扩展类的范围内, //getsupportedextensions实际上返回的是cachedclasses成员保存的类,是不包括adaptive注解的类和包装类的 //所以这里才不会发生循环调用adaptiveextensionfactory的构造器 for (string name : loader.getsupportedextensions()) { list.add(loader.getextension(name)); } factories = collections.unmodifiablelist(list); } @override public <t> t getextension(class<t> type, string name) { //遍历所有extensionfactory实现类, //返回第一个不是null的值 //我们需要看一下都有哪些extensionfactory实现类 //注意,这里思考一下,adaptiveextensionfactory为什么不会循环调用??? //这里仍然涉及到extensionfactory多个实现类排序的问题??? for (extensionfactory factory : factories) { t extension = factory.getextension(type, name); if (extension != null) { return extension; } } return null; }
}
这里仍然涉及到extensionfactory多个实现类排序的问题???
这里通过遍历所有的extensionfactory实现类,找到相应的属性值。extensionfactory实现类有myextensionfactory,spiextensionfactory,adaptiveextensionfactory,springextensionfactory,除去adaptiveextensionfactory与自适应机制相关,不起真正的依赖寻找的作用;spiextensionfactory是针对带有spi注解的类型进行自动注入依赖;
而对与一般的类型,则是使用springextensionfactory来进行依赖注入,在spring的beanfactory中查找匹配的bean实例,大概逻辑是:先通过beanname来查找,找不到再通过类型来查找。
实现aop特性
dubbo的aop的实现略显简单,使用静态代理模式,代理类由用户实现,可以通过多层包装实现多级拦截。
如果有多个包装类的情况下,同样存在顺序的问题, extensionloader有好多地方应该确定类的调用顺序,却没相应的排序规则,甚至都没有预留出排序接口,这点spring做得非常好,
spring中凡是涉及到多个平级类的链式调用或遍历查找的,都会实现ordered接口或priorityordered接口,包括aop中有多个通知类advice的情况,都会以一定的规则进行排序。
上一篇: PHP循环中进度展示以及"假死"
下一篇: 如何提高PHP编程效率?