经过本文的学习,能够了解 Dubbo SPI 的特性及实现原理,但愿对你们的开发设计有必定的启发性。java
SPI 全称为 Service Provider Interface,是一种模块间组件相互引用的机制。其方案一般是提供方将接口实现类的全名配置在classPath下的指定文件中,由调用方读取并加载。这样须要替换某个组件时,只须要引入新的JAR包并在其中包含新的实现类和配置文件便可,调用方的代码无需任何调整。优秀的SPI框架可以提供单接口多实现类时的优先级选择,由用户指定选择哪一个实现。spring
得益于这些能力,SPI对模块间的可插拔机制和动态扩展提供了很是好的支撑。数据库
本文将简单介绍JDK自带的SPI,分析SPI和双亲委派的关系,进而重点分析DUBBO的SPI机制;比较二者有何不一样,DUBBO的SPI带来了哪些额外的能力。apache
提供者在classPath或者jar包的META-INF/services/目录建立以服务接口命名的文件,调用者经过java.util.ServiceLoader加载文件内容中指定的实现类。bootstrap
search示例接口数组
package com.example.studydemo.spi; public interface Search { void search(); }
文件搜索实现类缓存
package com.example.studydemo.spi; public class FileSearchImpl implements Search { @Override public void search() { System.out.println("文件搜索"); } }
数据库搜索实现类架构
package com.example.studydemo.spi; public class DataBaseSearchImpl implements Search { @Override public void search() { System.out.println("数据库搜索"); } }
文件内容为:app
com.example.studydemo.spi.DataBaseSearchImpl com.example.studydemo.spi.FileSearchImpl
测试:框架
import java.util.ServiceLoader; public class JavaSpiTest { public static void main(String[] args) { ServiceLoader<Search> searches = ServiceLoader.load(Search.class); searches.forEach(Search::search); } }
结果为:
2. 简单分析
ServiceLoader做为JDK提供的一个服务实现查找工具类,调用自身load方法加载Search接口的全部实现类,而后可使用for循环遍历实现类进行方法调用。
有一个疑问:META-INF/services/目录是硬编码的吗,其它路径行不行?答案是不行。
跟进到ServiceLoader类中,第一行代码就是private static final String PREFIX = “META-INF/services/”,因此SPI配置文件只能放在classPath或者jar包的这个指定目录下面。
ServiceLoader的文件载入路径
public final class ServiceLoader<S> implements Iterable<S> { //硬编码写死了文件路径 private static final String PREFIX = "META-INF/services/"; // The class or interface representing the service being loaded private final Class<S> service; // The class loader used to locate, load, and instantiate providers private final ClassLoader loader;
JDK SPI的使用比较简单,作到了基本的加载扩展组件的功能,但有如下几点不足:
基于类加载的双亲委派原则,由JDK内部加载的class默认应该归属于bootstrap类加载器,那么SPI机制加载的class是否也属于bootstrap呢 ?
答案是否认的,原生SPI机制经过ServiceLoader.load方法由外部指定类加载器,或者默认取Thread.currentThread().getContextClassLoader()线程上下文的类加载器,从而避免了class被载入bootstrap加载器。
双亲委派的本质涵义是在rt.jar包和外部class之间创建一道classLoader的鸿沟,即rt.jar内的class不该由外部classLoader加载,外部class不该由bootstrap加载。
SPI仅是提供了一种在JDK代码内部干预外部class文件加载的机制,并未强制指定加载到何处;外部的class仍是由外部的classLoader加载,未跨越这道鸿沟,也就谈不上破坏双亲委派。
原生ServiceLoader的类加载器
//指定类加载器 public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader) //默认取前线程上下文的类加载器 public static <S> ServiceLoader<S> load(Class<S> service)
Dubbo借鉴了Java SPI的思想,与JDK的ServiceLoader相对应的,Dubbo设计了ExtensionLoader类,其提供的功能比JDK更为强大。
首先介绍一些基本概念,让你们有一个初步的认知。
自适应扩展实例其实就是一个扩展类的代理对象,它实现了扩展点接口。在调用扩展点的接口方法时,会根据实际的参数来决定要使用哪一个扩展。好比一个Search的扩展点,有一个search方法。有两个实现FileSearchImpl和DataBaseSearchImpl。Search的自适应实例在调用接口方法的时候,会根据search方法中的参数,来决定要调用哪一个Search的实现。
若是方法参数中有name=FileSearchImpl,那么就调用FileSearchImpl的search方法。若是name=DataBaseSearchImpl,就调用DataBaseSearchImpl的search方法。 自适应扩展实例在Dubbo中的使用很是普遍。
在Dubbo中每个扩展点均可以有自适应的实例,若是咱们没有使用@Adaptive人工指定,Dubbo会使用字节码工具自动生成一个。
SPI Annotation
做用于扩展点的接口上,代表该接口是一个扩展点,能够被Dubbo的ExtentionLoader加载
@Adaptive注解可使用在类或方法上。用在方法上表示这是一个自适应方法,Dubbo生成自适应实例时会在方法中植入动态代理的代码。方法内部会根据方法的参数来决定使用哪一个扩展。@Adaptive注解用在类上表明该实现类是一个自适应类,属于人为指定的场景,Dubbo就不会为该SPI接口生成代理类,最典型的应用如AdaptiveCompiler、AdaptiveExtensionFactory等。
@Adaptive注解的值为字符串数组,数组中的字符串是key值,代码中要根据key值来获取对应的Value值,进而加载相应的extension实例。好比new String[]{“key1”,”key2”},表示会先在URL中寻找key1的值,
若是找到则使用此值加载extension,若是key1没有,则寻找key2的值,若是key2也没有,则使用SPI注解的默认值,若是SPI注解没有默认值,则将接口名按照首字母大写分红多个部分,
而后以’.’分隔,例如org.apache.dubbo.xxx.YyyInvokerWrapper接口名会变成yyy.invoker.wrapper,而后以此名称作为key到URL寻找,若是仍没有找到则抛出IllegalStateException异常。
定义一个接口,标注上dubbo的SPI注解,赋予默认值,并提供两个extension实现类
package com.example.studydemo.spi; @SPI("dataBase") public interface Search { void search(); }
public class FileSearchImpl implements Search { @Override public void search() { System.out.println("文件搜索"); } }
public class DataBaseSearchImpl implements Search { @Override public void search() { System.out.println("数据库搜索"); } }
在META-INF/dubbo 路径下建立Search文件
文件内容以下:
dataBase=com.example.studydemo.spi.DataBaseSearchImpl file=com.example.studydemo.spi.FileSearchImpl
编写测试类进行测试,内容以下:
public class DubboSpiTest { public static void main(String[] args) { ExtensionLoader<Search> extensionLoader = ExtensionLoader.getExtensionLoader(Search.class); Search fileSearch = extensionLoader.getExtension("file"); fileSearch.search(); Search dataBaseSearch = extensionLoader.getExtension("dataBase"); dataBaseSearch.search(); System.out.println(extensionLoader.getDefaultExtensionName()); Search defaultSearch = extensionLoader.getDefaultExtension(); defaultSearch.search(); } }
结果为:
从代码示例上来看,Dubbo SPI与Java SPI在这几方面是相似的:
下面深刻到源码看看SPI在Dubbo中是怎样工做的,以Protocol接口为例进行分析。
//一、获得Protocol的扩展加载对象extensionLoader,由这个加载对象得到对应的自适应扩展类 Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); //二、根据扩展名获取对应的扩展类 Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
在获取扩展实例前要先获取Protocol接口的ExtensionLoader组件,经过ExtensionLoader来获取相应的Protocol实例Dubbo实际是为每一个SPI接口都建立了一个对应的ExtensionLoader。
ExtensionLoader组件
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!"); } //EXTENSION_LOADERS为ConcurrentMap,存储Class对应的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; }
EXTENSION_LOADERS是一个 ConcurrentMap,以接口Protocol为key,以ExtensionLoader对象为value;保存的是Protocol扩展的加载类,第一次加载的时候Protocol尚未本身的接口加载类,须要实例化一个。
再看new ExtensionLoader<T>(type) 这个操做,下面为ExtensionLoader的构造方法:
rivate ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
每个ExtensionLoader都包含2个值:type和objectFactory,此例中type就是Protocol,objectFactory就是ExtensionFactory。
对于ExtensionFactory接口来讲,它的加载类中objectFactory值为null。
对于其余的接口来讲,objectFactory都是经过ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()来获取;objectFactory的做用就是为dubbo的IOC提供依赖注入的对象,能够认为是进程内多个组件容器的一个上层引用,
随着这个方法的调用次数愈来愈多,EXTENSION_LOADERS 中存储的 loader 也会愈来愈多。
自适应扩展类与IOC
获得ExtensionLoader组件以后,再看如何得到自适应扩展实例。
public T getAdaptiveExtension() { //cachedAdaptiveInstance为缓存的自适应对象,第一次调用时尚未建立自适应类,因此instance为null 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; }
首先从cachedAdaptiveInstance缓存中获取,第一次调用时尚未相应的自适应扩展,须要建立自适应实例,建立后再将该实例放到cachedAdaptiveInstance缓存中。
建立自适应实例参考createAdaptiveExtension方法,该方法包含两部份内容:建立自适应扩展类并利用反射实例化、利用IOC机制为该实例注入属性。
private T createAdaptiveExtension() { try { //获得自适应扩展类并利用反射实例化,而后注入属性值 return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); } }
再来分析getAdaptiveExtensionClass方法,以Protocol接口为例,该方法会作如下事情:获取全部实现Protocol接口的扩展类、若是有自适应扩展类直接返回、若是没有则建立自适应扩展类。
//该动态代理生成的入口 private Class<?> getAdaptiveExtensionClass() { //1.获取全部实现Protocol接口的扩展类 getExtensionClasses(); //2.若是有自适应扩展类,则返回 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //3.若是没有,则建立自适应扩展类 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
getExtensionClasses方法会加载全部实现Protocol接口的扩展类,首先从缓存中获取,缓存中没有则调用loadExtensionClasses方法进行加载并设置到缓存中,以下图所示:
private Map<String, Class<?>> getExtensionClasses() { //从缓存中获取 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { //从SPI配置文件中解析 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
loadExtensionClasses方法以下:首先获取SPI注解中的value值,做为默认扩展名称,在Protocol接口中SPI注解的value为dubbo,所以DubboProtocol就是Protocol的默认实现扩展。其次加载三个配置路径下的全部的Protocol接口的扩展实现。
// 此方法已经getExtensionClasses方法同步过。 private Map<String, Class<?>> loadExtensionClasses() { 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; } private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
在加载配置路径下的实现中,其中有一个须要关注的点,若是其中某个实现类上有Adaptive注解,说明用户指定了自适应扩展类,那么该实现类就会被赋给cachedAdaptiveClass,在getAdaptiveExtensionClass方法中会被直接返回。
若是该变量为空,则须要经过字节码工具来建立自适应扩展类。
private Class<?> createAdaptiveExtensionClass() { //生成类代码 String code = createAdaptiveExtensionClassCode(); //找到类加载器 ClassLoader classLoader = findClassLoader(); //获取编译器实现类,此处为AdaptiveCompiler,此类上有Adaptive注解 com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); //将类代码编译为Class return compiler.compile(code, classLoader); }
createAdaptiveExtensionClass方法生成的类代码以下:
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative 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.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); } }
由字节码工具生成的类Protocol$Adpative在方法末尾调用了ExtensionLoader.getExtensionLoader(xxx).getExtension(extName)来知足adaptive的自适应动态特性。
传入的extName就是从url中获取的动态参数,用户只须要在表明DUBBO全局上下文信息的URL中指定protocol参数的取值,adaptiveExtentionClass就能够去动态适配不一样的扩展实例。
再看属性注入方法injectExtension,针对public的只有一个参数的set方法进行处理,利用反射进行方法调用来实现属性注入,此方法是Dubbo SPI实现IOC功能的关键。
private T injectExtension(T instance) { try { 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); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance;
Dubbo IOC 是经过set方法注入依赖,Dubbo首先会经过反射获取到实例的全部方法,而后再遍历方法列表,检测方法名是否具备set方法特征。如有则经过ObjectFactory获取依赖对象。
最后经过反射调用set方法将依赖设置到目标对象中。objectFactory在建立加载类ExtensionLoader的时候已经建立了,由于@Adaptive是打在类AdaptiveExtensionFactory上,因此此处就是AdaptiveExtensionFactory。
AdaptiveExtensionFactory持有全部ExtensionFactory对象的集合,dubbo内部默认实现的对象工厂是SpiExtensionFactory和SpringExtensionFactory,他们通过TreeSet排好序,查找顺序是优先先从SpiExtensionFactory获取,若是返回空在从SpringExtensionFactory获取。
//有Adaptive注解说明该类是自适应类,不须要程序本身建立代理类 @Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { //factories拥有全部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); } //查找时会遍历factories,顺序优先从SpiExtensionFactory中获取,再从SpringExtensionFactory中获取,缘由为初始化时getSupportedExtensions方法中使用TreeSet已经排序,见下图 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; } }
public Set<String> getSupportedExtensions() { Map<String, Class<?>> clazzes = getExtensionClasses(); return Collections.unmodifiableSet(new TreeSet<String>(clazzes.keySet())); }
虽然有过分设计的嫌疑,但咱们不得不佩服dubbo SPI设计的精巧。
扩展实例和AOP
getExtension方法比较简单,重点在于createExtension方法,根据扩展名建立扩展实例。
public T getExtension(String name) { 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; }
createExtension方法中的部份内容上文已经分析过了,getExtensionClasses方法获取接口的全部实现类,而后经过name获取对应的Class。紧接着经过clazz.newInstance()来实例化该实现类,调用injectExtension为实例注入属性。
private T createExtension(String name) { //getExtensionClasses方法以前已经分析过,获取全部的扩展类,而后根据扩展名获取对应的扩展类 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, (T) clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //属性注入 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && wrapperClasses.size() > 0) { 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); } }
在方法的最后有一段对于WrapperClass包装类的处理逻辑,若是接口存在包装类实现,那么就会返回包装类实例。实现AOP的关键就是WrapperClass机制,判断一个扩展类是不是WrapperClass的依据,是看其constructor函数中是否包含当前接口参数。
若是有就认为是一个wrapperClass,最终建立的实例是一个通过多个wrapperClass层层包装的结果;在每一个wrapperClass中均可以编入面向切面的代码,从而就简单实现了AOP功能。
Activate活性扩展
对应ExtensionLoader的getActivateExtension方法,根据多个过滤条件从extension集合中智能筛选出您所需的那一部分。
getActivateExtension方法
public List<T> getActivateExtension(URL url, String[] names, String group);
首先这个方法只会返回带有Activate注解的扩展类,但并不是带有注解的扩展类都会被返回。
names是明确指定所须要的那部分扩展类,非明确指定的扩展类须要知足group过滤条件和Activate注解自己指定的key过滤条件,非明确指定的会按照Activate注解中指定的排序规则进行排序;
getActivateExtension的返回结果是上述两种扩展类的总和。
Activate注解类
*/ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { /** * Group过滤条件。 */ String[] group() default {}; /** * Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。 */ String[] value() default {}; /** * 排序信息,能够不提供。 */ String[] before() default {}; /** * 排序信息,能够不提供。 */ String[] after() default {}; /** * 排序信息,能够不提供。 */ int order() default 0; }
活性Extension最典型的应用是rpc invoke时获取filter链条,各类filter有明确的执行优先级,同时也能够人为增添某些filter,filter还能够根据服务提供者和消费者进行分组过滤。
Dubbo invoke获取filter链条
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), Constants.SERVICE_FILTER_KEY, Constants.PROVIDER);
以TokenFilter为例,其注解为@Activate(group = Constants.PROVIDER, value = Constants.TOKEN_KEY),表示该过滤器只在服务提供方才会被加载,同时会验证注册地址url中是否带了token参数,若是有token表示服务端注册时指明了要作token验证,天然就须要加载该filter。
反之则不用加载;此filter加载后的执行逻辑则是从url中获取服务端注册时预设的token,再从rpc请求的attachments中获取消费方设置的remote token,比较二者是否一致,若不一致抛出RPCExeption异常阻止消费方的正常调用。
Dubbo 全部的接口几乎都预留了扩展点,根据用户参数来适配不一样的实现。若是想增长新的接口实现,只须要按照SPI的规范增长配置文件,并指向新的实现便可。
用户配置的Dubbo属性都会体如今URL全局上下文参数中,URL贯穿了整个Dubbo架构,是Dubbo各个layer组件间相互调用的纽带。
总结一下 Dubbo SPI 相对于 Java SPI 的优点:
做者: Xie Xiaopeng