一. 概述 本文主要分享 Dubbo 的拓展机制 SPI 。
想要理解 Dubbo ,理解 Dubbo SPI 是非常必须的。在 Dubbo 中,提供了大量的拓展点 ,基于 Dubbo SPI 机制加载
Dubbo SPI官方文档:Dubbo SPI 概述 | Apache Dubbo
本文基于 Dubbo 2.7.10 版本源码
二. Dubbo SPI特性 在看具体的 Dubbo SPI 实现之前,我们先理解 Dubbo SPI 产生的背景:
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
用户能够基于 Dubbo 提供的扩展能力,很方便基于自身需求扩展其他协议、过滤器、路由等。下面介绍下 Dubbo 扩展能力的特性。
按需加载。Dubbo 的扩展能力不会一次性实例化所有实现,而是用那个扩展类则实例化那个扩展类,减少资源浪费。
增加扩展类的 IOC 能力。Dubbo 的扩展能力并不仅仅只是发现扩展服务实现类,而是在此基础上更进一步,如果该扩展类的属性依赖其他对象,则 Dubbo 会自动的完成该依赖对象的注入功能。
增加扩展类的 AOP 能力。Dubbo 扩展能力会自动的发现扩展类的包装类,完成包装类的构造,增强扩展类的功能。
具备动态选择扩展实现的能力。Dubbo 扩展会基于参数,在运行时动态选择对应的扩展类,提高了 Dubbo 的扩展能力。
可以对扩展实现进行排序。能够基于用户需求,指定扩展实现的执行顺序。
提供扩展点的 Adaptive 能力。该能力可以使的一些扩展类在 consumer 端生效,一些扩展类在 provider 端生效。
从 Dubbo 扩展的设计目标可以看出,Dubbo 实现的一些例如动态选择扩展实现、IOC、AOP 等特性,能够为用户提供非常灵活的扩展能力。
三. 代码结构 Dubbo SPI 在 dubbo-common
的 org.apache.dubbo.common.extension
包实现,如下图所示:
四. ExtensionLoader org.apache.dubbo.common.extension.ExtensionLoader
,拓展加载器。这是 Dubbo SPI 的核心 。
4.1 属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap <>(64 ); private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap <>(64 ); private final Class<?> type; private final ExtensionFactory objectFactory; private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap <>(); private final Holder<Map<String, Class<?>>> cachedClasses = new Holder <>(); private final Map<String, Object> cachedActivates = new ConcurrentHashMap <>(); private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap <>(); private final Holder<Object> cachedAdaptiveInstance = new Holder <>(); private volatile Class<?> cachedAdaptiveClass = null ; private String cachedDefaultName; private volatile Throwable createAdaptiveInstanceError; private Set<Class<?>> cachedWrapperClasses; private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap <>(); private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
我们将属性分成了两类:1)静态属性;2)对象属性。这是为啥呢?
【静态属性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器 。一个拓展( 拓展接口 )对应一个 ExtensionLoader 对象。例如,Protocol 和 Filter 分别 对应一个 ExtensionLoader 对象。
【对象属性】另一方面,一个拓展通过其 ExtensionLoader 对象,加载它的拓展实现们。我们会发现多个属性都是 “cached” 开头。ExtensionLoader 考虑到性能和资源的优化,读取拓展配置后,会首先进行缓存 。等到 Dubbo 代码真正 用到对应的拓展实现时,进行拓展实现的对象的初始化。并且,初始化完成后,也会进行缓存 。也就是说:
4.2 获得拓展配置 4.2.1 getExtensionClasses 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 private final Holder<Map<String, Class<?>>> cachedClasses = new Holder <>(); private volatile Class<?> cachedAdaptiveClass = null ; private Set<Class<?>> cachedWrapperClasses; 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; }
cachedClasses属性,缓存的拓展实现类集合。它不包含如下两种类型的拓展实现:
自适应拓展实现类。例如 AdaptiveExtensionFactory 。
拓展 Adaptive 实现类,会添加到 cachedAdaptiveClass
属性中。
带唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。
拓展 Wrapper 实现类,会添加到 cachedWrapperClasses
属性中。
总结来说,cachedClasses
+ cachedAdaptiveClass
+ cachedWrapperClasses
才是完整 缓存的拓展实现类的配置。
4.2.2 loadExtensionClasses 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap <>(); for (LoadingStrategy strategy : strategies) { loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache" , "com.alibaba" ), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); } return extensionClasses; }
第一步:加载默认的扩展名
第二步:遍历加载策略数组,去加载不同文件夹下的扩展。
strategies属性是在启动时,在 loadLoadingStrategies
方法中通过 Java SPI 加载的 LoadingStrategy
接口的实现类。
1 2 3 4 5 6 7 private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();private static LoadingStrategy[] loadLoadingStrategies() { return stream(load(LoadingStrategy.class).spliterator(), false ) .sorted() .toArray(LoadingStrategy[]::new ); }
它会读取 META-INF/services
下的 org.apache.dubbo.common.extension.LoadingStrategy
文件中注册的实现类:
这些实现类中会指定,需要加载的拓展的配置文件路径:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class DubboInternalLoadingStrategy implements LoadingStrategy { @Override public String directory () { return "META-INF/dubbo/internal/" ; } @Override public int getPriority () { return MAX_PRIORITY; } }
loadDirectory方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 private void loadDirectory (Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { String fileName = dir + type; try { Enumeration<java.net.URL> urls = null ; ClassLoader classLoader = findClassLoader(); if (extensionLoaderClassLoaderFirst) { ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) { urls = extensionLoaderClassLoader.getResources(fileName); } } if (urls == null || !urls.hasMoreElements()) { if (classLoader != null ) { 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, overridden, excludedPackages); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ")." , t); } }
loadResource方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 private void loadResource (Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String... excludedPackages) { try { try (BufferedReader reader = new BufferedReader (new InputStreamReader (resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; String clazz = 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 = line.substring(0 , i).trim(); clazz = line.substring(i + 1 ).trim(); } else { clazz = line; } if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) { loadClass(extensionClasses, resourceURL, Class.forName(clazz, true , classLoader), name, overridden); } } 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); } }
loadClass方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private void loadClass (Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) 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." ); } if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0 ) { throw new IllegalStateException ("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0 ]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n, overridden); } } } }
4.3 获得拓展加载器 在 Dubbo 的代码里,常常能看到如下的代码:
1 ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.3.1 getExtensionLoader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 an interface!" ); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException ("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!" ); } 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; }
4.3.2 构造方法 构造方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private final Class<?> type;private final ExtensionFactory objectFactory;private ExtensionLoader (Class<?> type) { this .type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
objectFactory
属性,对象工厂,功能上和 Spring IOC 一致 。
用于调用 #injectExtension(instance)
方法时,向创建的拓展注入其依赖的属性。例如,CacheFilter.cacheFactory
属性。
第 17 行:当拓展接口非 ExtensionFactory 时(如果不加这个判断,会是一个死循环),调用 ExtensionLoader#getAdaptiveExtension()
方法,获得 ExtensionFactory 拓展接口的自适应 拓展实现对象。为什么呢 ?在 后文详细解释。
4.4 获得指定拓展对象 在 Dubbo 的代码里,常常能看到如下的代码:
1 ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.4.1 getExtension 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @SuppressWarnings("unchecked") public T getExtension (String name) { return getExtension(name, true ); } public T getExtension (String name, boolean wrap) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException ("Extension name == null" ); } if ("true" .equals(name)) { return getDefaultExtension(); } final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null ) { synchronized (holder) { instance = holder.get(); if (instance == null ) { instance = createExtension(name, wrap); holder.set(instance); } } } return (T) instance; }
4.4.2 createExtension #createExtension(name)
方法,创建拓展名的拓展对象,并缓存。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 @SuppressWarnings("unchecked") private T createExtension (String name, boolean wrap) { 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, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); if (wrap) { List<Class<?>> wrapperClassesList = new ArrayList <>(); if (cachedWrapperClasses != null ) { wrapperClassesList.addAll(cachedWrapperClasses); wrapperClassesList.sort(WrapperComparator.COMPARATOR); Collections.reverse(wrapperClassesList); } if (CollectionUtils.isNotEmpty(wrapperClassesList)) { for (Class<?> wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); if (wrapper == null || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } } } initExtension(instance); return instance; } catch (Throwable t) { throw new IllegalStateException ("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }
Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个,也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。
4.4.3 injectExtension注入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private T injectExtension (T instance) { if (objectFactory == null ) { return instance; } try { for (Method method : instance.getClass().getMethods()) { if (!isSetter(method)) { continue ; } 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; }
第 28 行:获得属性值 。注意 ,此处虽然调用的是 ExtensionFactory#getExtension(type, name)
方法,实际获取的不仅仅是拓展对象,也可以是 Spring Bean 对象。
4.5 获得自适应的拓展对象 在 Dubbo 的代码里,常常能看到如下的代码:
1 ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
友情提示,胖友先看下 「6. Adaptive」 的内容再回到此处。
Dubbo 自适应拓展的作用可以参考:SPI 自适应拓展 | Apache Dubbo
4.5.1 getAdaptiveExtension 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @SuppressWarnings("unchecked") public T getAdaptiveExtension () { Object instance = cachedAdaptiveInstance.get(); if (instance == null ) { if (createAdaptiveInstanceError != null ) { throw new IllegalStateException ("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null ) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException ("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; }
4.5.2 createAdaptiveExtension 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SuppressWarnings("unchecked") private T createAdaptiveExtension () { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException ("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
4.5.3 getAdaptiveExtensionClass #getAdaptiveExtensionClass()
方法,获得自适应拓展类。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null ) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
4.5.4 createAdaptiveExtensionClass 1 2 3 4 5 6 7 8 9 10 11 12 13 private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator (type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
第 5 行会生成自适应拓展的代码实现,然后会编译字符串生成 Class。我们以 org.apache.dubbo.rpc.cluster.Cluster
接口为例,它生成的自适应拓展实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package org.apache.dubbo.rpc.cluster;import org.apache.dubbo.common.extension.ExtensionLoader;public class Cluster$Adaptive implements org .apache.dubbo.rpc.cluster.Cluster { public org.apache.dubbo.rpc.cluster.Cluster getCluster ( java.lang.String arg0) { throw new UnsupportedOperationException ( "The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(java.lang.String) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!" ); } public org.apache.dubbo.rpc.cluster.Cluster getCluster ( java.lang.String arg0, boolean arg1) { throw new UnsupportedOperationException ( "The method public static org.apache.dubbo.rpc.cluster.Cluster org.apache.dubbo.rpc.cluster.Cluster.getCluster(java.lang.String,boolean) of interface org.apache.dubbo.rpc.cluster.Cluster is not adaptive method!" ); } public org.apache.dubbo.rpc.Invoker join ( org.apache.dubbo.rpc.cluster.Directory arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null ) { throw new IllegalArgumentException ( "org.apache.dubbo.rpc.cluster.Directory argument == null" ); } if (arg0.getUrl() == null ) { throw new IllegalArgumentException ( "org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null" ); } org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = url.getParameter("cluster" , "failover" ); if (extName == null ) { throw new IllegalStateException ( "Failed to get extension (org.apache.dubbo.rpc.cluster.Cluster) name from url (" + url.toString() + ") use keys([cluster])" ); } org.apache.dubbo.rpc.cluster.Cluster extension = (org.apache.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.cluster.Cluster.class) .getExtension(extName); return extension.join(arg0); } }
生成的代码中,就是自适应拓展实现的核心,它会根据请求URL的参数,去动态加载对应的Cluster实现,完成不同的集群容错策略。
4.6 获得激活的拓展对象数组 在 Dubbo 的代码里,看到使用代码如下:
1 List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
4.6.1 getActivateExtension #getActivateExtension(url, key, group)
方法,获得符合自动激活条件的拓展对象数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public List<T> getActivateExtension (URL url, String key, String group) { String value = url.getParameter(key); return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group); } public List<T> getActivateExtension (URL url, String[] values, String group) { List<T> activateExtensions = new ArrayList <>(); List<String> names = values == null ? new ArrayList <>(0 ) : asList(values); if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) { activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group(); activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value(); } else { continue ; } if (isMatchGroup(group, activateGroup) && !names.contains(name) && !names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url)) { activateExtensions.add(getExtension(name)); } } activateExtensions.sort(ActivateComparator.COMPARATOR); } List<T> loadedExtensions = new ArrayList <>(); for (int i = 0 ; i < names.size(); i++) { String name = names.get(i); if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) { if (DEFAULT_KEY.equals(name)) { if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(0 , loadedExtensions); loadedExtensions.clear(); } } else { loadedExtensions.add(getExtension(name)); } } } if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(loadedExtensions); } return activateExtensions; }
五. @SPI org.apache.dubbo.common.extension.SPI
,扩展点接口的标识。代码如下:
1 2 3 4 5 6 7 8 9 10 11 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface SPI { String value () default "" ; }
六. @Adaptive org.apache.dubbo.common.extension.Adaptive
,自适应拓展信息的标记。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package org.apache.dubbo.common.extension;import org.apache.dubbo.common.URL;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { String[] value() default {}; }
@Adaptive
注解,可添加类 或方法 上,分别代表了两种不同的使用方式。
友情提示:一个拓展接口,有且仅有一个 Adaptive 拓展实现类。
第一种,标记在类 上,代表手动实现 (代码中声明一个类)它是一个拓展接口的 Adaptive 拓展实现类。目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory 有这么用。
第二种,标记在拓展接口的方法上,代表自动生成代码实现该接口的 Adaptive 拓展实现类(参考:[「4.5.4 createAdaptiveExtensionClassCode」](# 4.5.4 createAdaptiveExtensionClass))。
七. @Activate org.apache.dubbo.common.extension.Activate
,自动激活条件的标记。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { String[] group() default {}; String[] value() default {}; @Deprecated String[] before() default {}; @Deprecated String[] after() default {}; int order () default 0 ; }
对于可以被框架中自动激活加载扩展,@Activate
用于配置扩展被自动激活加载条件。比如,Filter 扩展,有多个实现,使用 @Activate
的扩展可以根据条件 被自动加载。
分成过滤条件和排序信息两类属性 ,大家可以看下代码里的注释。
在 [「4.6 获得激活的拓展对象数组」](#4.6 获得激活的拓展对象数组) 详细解析。
八. ExtensionFactory org.apache.dubbo.common.extension.ExtensionFactory
,拓展工厂接口。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @SPI public interface ExtensionFactory { <T> T getExtension (Class<T> type, String name) ; }
ExtensionFactory 自身也是拓展接口,基于 Dubbo SPI 加载具体拓展实现类。
#getExtension(type, name)
方法,在 [「4.4.3 injectExtension」](#4.4.3 injectExtension) 中,获得拓展对象,向创建的拓展对象注入依赖属性 。在实际代码中,我们可以看到不仅仅获得的是拓展对象,也可以是 Spring 中的 Bean 对象。
ExtensionFactory 子类类图如下:
8.1 AdaptiveExtensionFactory org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
,自适应 ExtensionFactory 拓展实现类。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Adaptive public class AdaptiveExtensionFactory implements 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); } @Override 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 ; } }
@Adaptive
注解,为 ExtensionFactory 的自适应 拓展实现类。
构造 方法,使用 ExtensionLoader 加载 ExtensionFactory 拓展对象的实现类。若胖友没自己实现 ExtensionFactory 的情况下,factories
为 SpiExtensionFactory 和 SpringExtensionFactory 。
#getExtension(type, name)
方法,遍历 factories
,调用其 #getExtension(type, name)
方法,直到获得到属性值。
8.2 SpiExtensionFactory org.apache.dubbo.common.extension.factory.SpiExtensionFactory
,SPI ExtensionFactory 拓展实现类。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension (Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null ; } }
8.3 SpringExtensionFactory org.apache.dubbo.config.spring.extension.SpringExtensionFactory
,Spring ExtensionFactory 拓展实现类。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 package org.apache.dubbo.config.spring.extension;import org.apache.dubbo.common.extension.ExtensionFactory;import org.apache.dubbo.common.extension.SPI;import org.apache.dubbo.common.logger.Logger;import org.apache.dubbo.common.logger.LoggerFactory;import org.apache.dubbo.common.utils.ConcurrentHashSet;import org.springframework.context.ApplicationContext;import org.springframework.context.ConfigurableApplicationContext;import java.util.Set;import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean;public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet <ApplicationContext>(); public static void addApplicationContext (ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); } } public static void removeApplicationContext (ApplicationContext context) { CONTEXTS.remove(context); } public static Set<ApplicationContext> getContexts () { return CONTEXTS; } public static void clearContexts () { CONTEXTS.clear(); } @Override @SuppressWarnings("unchecked") public <T> T getExtension (Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null ; } for (ApplicationContext context : CONTEXTS) { T bean = getOptionalBean(context, name, type); if (bean != null ) { return bean; } } return null ; } }
例子:
DemoFilter 是笔者实现的 Filter 拓展实现类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class DemoFilter implements Filter { private DemoDAO demoDAO; @Override public Result invoke (Invoker<?> invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } public DemoFilter setDemoDAO (DemoDAO demoDAO) { this .demoDAO = demoDAO; return this ; } }
本文参考至:
芋道源码
Dubbo SPI | Apache Dubbo