上一章咱们介绍了JDK的SPI机制,它旨在创建一种服务发现的规范。而Dubbo基于此根据框架的总体设计作了一些改进:java
Dubbo做为一个微内核+插件的框架设计,其内核就是基于SPI机制动态地组装插件。插件都是以接口的方式定义在框架中,每一个接口提供的服务也称为扩展点,表示每一个服务接口均可以根据不一样的条件进行动态扩展,Dubbo的扩展点接口以@SPI注解标识。Dubbo自身的功能也是经过扩展点实现的,也就是Dubbo的全部功能点均可以被用户自定义扩展所代替。那么Dubbo的SPI机制到底是怎么工做的呢?app
在classpath下放置META-INF/dubbo/以接口全限定名定义的文件,内容为配置名=扩展实现类全限定名,多个实现类用换行符分隔。框架
META-INF/dubbo目录是针对二次开发者的,dubbo自身加载扩展点配置的目录有三个,依次是:ide
以Dubbo的协议接口Protocol为例,在dubbo-rpc-default模块中定义了DubboProtocol实现,目录为url
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
文件中内容为spa
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
对应于dubbo的xml配置,即.net
<dubbo:protocol name="dubbo" />
JDK自带的SPI机制使用ServiceLoader加载和获取,Dubbo对于扩展点的的加载和获取则是使用ExtensionLoader。大部分的扩展点都是经过ExtensionLoader预加载一个适配类,以Protocol为例,调用方式以下:插件
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
getAdaptiveExtension方法建立扩展点的适配类,建立的方式分为两种设计
对于1的方式很好理解,好比Compiler接口,它的实现类AdaptiveCompiler类上就有@Adaptive注解netty
@Adaptive public class AdaptiveCompiler implements Compiler { // 实现代码省略 }
2的方式就较为复杂,仍是以Protocol举例,先看下Protocol接口的源码:
@SPI("dubbo") public interface Protocol { int getDefaultPort(); @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; void destroy(); }
Protocol接口定义了四个方法,其中export和refer方法都有@Adaptive注解,不一样于1中类上的注解,这里的注解是在方法上,这个地方要着重强调一下@Adaptive注解做用的位置。对于实现类上没有此注解,而方法上有此注解的,适用于2方式。Protocol基于2方式生成的代码以下:
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol { // 没有@Adaptive注解的直接抛出UnsupportedOperationException 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!"); } // 有@Adaptive的方法,从参数里获取URL对象, // 获取扩展点配置名,ExtensionLoader.getExtension(extName)匹配建立扩展点实现类对象 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); } 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); } }
生成的适配类对象,以扩展点接口+$Adaptive命名(如Protocol$Adaptive),实现了扩展点接口。没有@Adaptive的方法直接抛出UnsupportedOperationException,对于有@Adaptive的方法,从参数中获取URL对象,而后根据URL获取扩展点配置名,再使用ExtensionLoader.getExtension(extName)匹配建立扩展点实现类对象。
这里的URL是Dubbo自定义的类,它是Dubbo的统一数据模型,穿插于系统的整个执行过程。URL的数据格式以下
dubbo://192.168.2.100:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&owner=william&pid=7356&side=consumer×tamp=1416971340626
a. URL获取扩展点配置名,经过key从URL的parameter中获取
String extName = url.getParameter(key, defalutValue);
defalutValue取值于扩展点接口的@SPI的值,好比Protocol接口的defaultValue=dubbo
@SPI("dubbo") public interface Protocol {}
key的值来自于@Adaptive的value值,若是@Adaptive没有设置value,默认为扩展点接口的类的simpleName。若是key的值为protocol时特殊处理:
String extName = url.getProtocol()
好比Transporter扩展点的bind方法
@SPI("netty") public interface Transporter { @Adaptive({"server", "transporter"}) Server bind(URL url, ChannelHandler handler) throws RemotingException; }
defaultValue=netty,key=server,transporter,获取配置名的方法:
String extName = url.getParameter("server",url.getParameter("transporter", "netty"));
b. 扩展点建立完$Adaptive对象,具体调用时,根据URL参数获取的配置名extName,查询对应的扩展点实现,仍是以Protocol扩展点举例
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
getExtension(extName)方法匹配扩展点的实现类Class的配置名,找到对应的Class对象,执行newInstance方法建立实际的操做对象。extName=dubbo的Protocol扩展点实现类为DubboProtocol。建立完实现类对象,会对此对象执行injectExtension方法,即对对象内的以set开头,而且只有一个参数的public方法,执行IOC注入。
IOC注入也是以ExtensionFactory扩展点的方式实现,默认的方式是先以SPI机制获取方法参数类型的实现,若是此方法参数类型非接口或没有@SPI注解,则从Spring上下文中获取。
if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { 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) { method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } }
接下来判断扩展点的实现类中是否存在包装类(Wrapper),Wrapper类指存在参数为扩展点接口的构造方法的扩展点实现类。Wrapper类不是扩展点的真正实现,主要用于对真正的扩展点实现进行包装。好比Protocol的Filter包装类实现ProtocolFilterWrapper。
若是扩展点存在包装类的Class,反射调用其构造方法,将真正的扩展点实现传入,并再此执行injectExtension方法进行IOC注入。
Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } }
至此,扩展点的调用来到了真正对应的扩展点实现类对象中。
若是须要同时加载多个扩展点实现,可使用自动激活。当扩展点的实现类上有@Activate注解,标识它是一个激活类。@Activate能够配置group和value值,ExtensionLoader的getActivateExtension方法匹配并获取符合条件的全部激活类。Dubbo内部使用激活类的主要在Protocol的Filter和Listener,后面再详细介绍。
以上讨论的扩展点的适配类,包装类,激活类的实现,都要基于对SPI机制的配置文件的加载。Dubbo在获取扩展点的任何扩展时,都会先判断是否加载了配置文件,若是没有,即执行loadExtensionClasses方法加载,loadExtensionClasses方法中对Dubbo的三个配置目录分别加载,调用loadFile方法。
loadFile方法查找目录下扩展点接口的全限定名的文件,过滤掉"#"的注释内容,而后每行以"="分隔,获取配置名和扩展点实现类的全限定名。
依次判断是否为Adaptive适配类,是否为Wrapper包装类,是否为Activate激活类,并存储到对应的变量或集合中去。详细操做可见ExtensionLoader.loadFile方法。
最后贴上getExtension(name)方法的总体活动图(右击图片打开新标签页查看大图),来自大神的博客https://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235