本文基于 Dubbo 2.6.1 版本,望知悉。
🙂🙂🙂关注微信公众号:【芋道源码】 有福利:
RocketMQ / MyCAT / Sharding-JDBC 所有 源码分析文章列表
RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
您对于源码的疑问每条留言都 将得到认真 回复。甚至不知道如何读源码也可以请教噢 。
新的 源码解析文章实时 收到通知。每周更新一篇左右 。
认真的 源码交流微信群。
1. 概述
艿艿的友情提示:
这是一篇相对长的文章。
胖友可以带着这样的思维来理解 Dubbo SPI ,它提供了 Spring IOC、AOP 的功能。😈
本文主要分享 Dubbo 的拓展机制 SPI 。
想要理解 Dubbo ,理解 Dubbo SPI 是非常必须的。在 Dubbo 中,提供了大量的拓展点 ,基于 Dubbo SPI 机制加载。如下图所示:
Dubbo 拓展点
2. 改进 在看具体的 Dubbo SPI 实现之前,我们先理解 Dubbo SPI 产生的背景:
FROM 《Dubbo 开发指南 —— 拓展点加载》
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 自己实现了一套 SPI 机制,而不是使用 Java 标准的 SPI 。
第一点问题,Dubbo 有很多的拓展点,例如 Protocol、Filter 等等。并且每个拓展点有多种的实现,例如 Protocol 有 DubboProtocol、InjvmProtocol、RestProtocol 等等。那么使用 JDK SPI 机制,会初始化无用的拓展点及其实现,造成不必要的耗时与资源浪费。
第二点问题,【TODO 8009】ScriptEngine 没看明白,不影响本文理解。
第三点问题,严格来说,这不算问题,而是增加了功能特性 ,在下文我们会看到。
3. 代码结构 Dubbo SPI 在 dubbo-common
的 extension
包实现,如下图所示:
代码结构
4. ExtensionLoader com.alibaba.dubbo.common.extension.ExtensionLoader
,拓展加载器。这是 Dubbo SPI 的核心 。
4.1 属性 1 : private static final String SERVICES_DIRECTORY = "META-INF/services/" ; 2 : 3 : private static final String DUBBO_DIRECTORY = "META-INF/dubbo/" ; 4 : 5 : private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/" ; 6 : 7 : private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*" ); 8 : 9 : 10 : 11 : 16 : private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); 17 : 26 : private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); 27 : 28 : 29 : 30 : 34 : private final Class<?> type; 35 : 42 : private final ExtensionFactory objectFactory; 43 : 50 : private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); 51 : 61 : private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>(); 62 : 63 : 70 : private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); 71 : 83 : private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); 84 : 87 : private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); 88 : 93 : private volatile Class<?> cachedAdaptiveClass = null ; 94 : 99 : private String cachedDefaultName; 100 : 105 : private volatile Throwable createAdaptiveInstanceError;106 : 107 : 114 : private Set<Class<?>> cachedWrapperClasses;115 : 116 : 124 : private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
第 1 至 5 行:在 META-INF/dubbo/internal/
和 META-INF/dubbo/
目录下,放置 接口全限定名 配置文件,每行 内容为:拓展名=拓展实现类全限定名 。
META-INF/dubbo/internal/
目录下,从名字上可以看出,用于 Dubbo 内部 提供的拓展实现。下图是一个例子:META-INF/dubbo/internal/ 例子
META-INF/dubbo/
目录下,用于用户自定义 的拓展实现。
META-INF/service/
目录下,Java SPI 的配置目录。在 「4.2 加载拓展配置」 中,我们会看到 Dubbo SPI 对 Java SPI 做了兼容 。
第 7 行:NAME_SEPARATOR
,拓展名分隔符,使用逗号 。
第 9 至 124 行 ,我们将属性分成了两类:1)静态属性;2)对象属性。这是为啥呢?
【静态属性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器 。一个拓展( 拓展接口 )对应一个 ExtensionLoader 对象。例如,Protocol 和 Filter 分别 对应一个 ExtensionLoader 对象。
【对象属性】另一方面,一个拓展通过其 ExtensionLoader 对象,加载它的拓展实现们 。我们会发现多个属性都是 “cached “ 开头。ExtensionLoader 考虑到性能和资源的优化,读取拓展配置后,会首先进行缓存 。等到 Dubbo 代码真正 用到对应的拓展实现时,进行拓展实现的对象的初始化。并且,初始化完成后,也会进行缓存 。也就是说:
🙂 胖友先看下属性的代码注释,有一个整体的印象。下面我们在读实现代码时,会进一步解析说明。
考虑到胖友能更好的理解下面的代码实现,推荐先阅读下 《Dubbo 开发指南 —— 扩展点加载》 文档,建立下对 ExtensionLoader 特点的初步理解:
扩展点自动包装
扩展点自动装配
扩展点自适应
扩展点自动激活
4.2 获得拓展配置 4.2.1 getExtensionClasses #getExtensionClasses()
方法,获得拓展实现类数组。
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();private volatile Class<?> cachedAdaptiveClass = null ;private Set<Class<?>> cachedWrapperClasses; 1 : 6 : private Map<String, Class<?>> getExtensionClasses() { 7 : 8 : Map<String, Class<?>> classes = cachedClasses.get(); 9 : if (classes == null ) { 10 : synchronized (cachedClasses) { 11 : classes = cachedClasses.get(); 12 : if (classes == null ) { 13 : 14 : classes = loadExtensionClasses(); 15 : 16 : cachedClasses.set(classes); 17 : } 18 : } 19 : } 20 : return classes; 21 : }
cachedClasses
属性,缓存的拓展实现类集合。它不包含如下两种类型的拓展实现:
自适应 拓展实现类。例如 AdaptiveExtensionFactory 。
拓展 Adaptive 实现类,会添加到 cachedAdaptiveClass
属性中。
带唯一参数为拓展接口 的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。
拓展 Wrapper 实现类,会添加到 cachedWrapperClasses
属性中。
总结来说,cachedClasses
+ cachedAdaptiveClass
+ cachedWrapperClasses
才是完整 缓存的拓展实现类的配置。
第 7 至 11 行:从缓存中,获得拓展实现类数组。
第 12 至 14 行:当缓存不存在时,调用 #loadExtensionClasses()
方法,从配置文件中,加载拓展实现类数组。
第 16 行:设置加载的实现类数组,到缓存中。
4.2.2 loadExtensionClasses #loadExtensionClasses()
方法,从多个 配置文件中,加载拓展实现类数组。
1 : 9 : private Map<String, Class<?>> loadExtensionClasses() { 10 : 11 : final SPI defaultAnnotation = type.getAnnotation(SPI.class);12 : if (defaultAnnotation != null ) {13 : String value = defaultAnnotation.value();14 : if ((value = value.trim()).length() > 0 ) {15 : String[] names = NAME_SEPARATOR.split(value);16 : if (names.length > 1 ) {17 : throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()18 : + ": " + Arrays.toString(names));19 : }20 : if (names.length == 1 ) cachedDefaultName = names[0 ];21 : }22 : }23 : 24 : 25 : Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();26 : loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);27 : loadFile(extensionClasses, DUBBO_DIRECTORY);28 : loadFile(extensionClasses, SERVICES_DIRECTORY);29 : return extensionClasses;30 : }
第 10 至 22 行:通过 @SPI
注解,获得拓展接口对应的默认的 拓展实现类名。在 「5. @SPI」 详细解析。
第 25 至 29 行:调用 #loadFile(extensionClasses, dir)
方法,从配置文件中,加载拓展实现类数组。注意 ,此处配置文件的加载顺序。
4.2.3 loadFile #loadFile(extensionClasses, dir)
方法,从一个 配置文件中,加载拓展实现类数组。代码如下:
private volatile Class<?> cachedAdaptiveClass = null ;private Set<Class<?>> cachedWrapperClasses;private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>(); 1 : 7 : private void loadFile (Map<String, Class<?>> extensionClasses, String dir) { 8 : 9 : String fileName = dir + type.getName(); 10 : try { 11 : Enumeration<java.net.URL> urls; 12 : 13 : ClassLoader classLoader = findClassLoader(); 14 : if (classLoader != null ) { 15 : urls = classLoader.getResources(fileName); 16 : } else { 17 : urls = ClassLoader.getSystemResources(fileName); 18 : } 19 : 20 : if (urls != null ) { 21 : while (urls.hasMoreElements()) { 22 : java.net.URL url = urls.nextElement(); 23 : try { 24 : BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8" )); 25 : try { 26 : String line; 27 : while ((line = reader.readLine()) != null ) { 28 : 29 : final int ci = line.indexOf('#' ); 30 : if (ci >= 0 ) line = line.substring(0 , ci); 31 : line = line.trim(); 32 : if (line.length() > 0 ) { 33 : try { 34 : 35 : String name = null ; 36 : int i = line.indexOf('=' ); 37 : if (i > 0 ) { 38 : name = line.substring(0 , i).trim(); 39 : line = line.substring(i + 1 ).trim(); 40 : } 41 : if (line.length() > 0 ) { 42 : 43 : Class<?> clazz = Class.forName(line, true , classLoader); 44 : if (!type.isAssignableFrom(clazz)) { 45 : throw new IllegalStateException("Error when load extension class(interface: " + 46 : type + ", class line: " + clazz.getName() + "), class " 47 : + clazz.getName() + "is not subtype of interface." ); 48 : } 49 : 50 : if (clazz.isAnnotationPresent(Adaptive.class)) { 51 : if (cachedAdaptiveClass == null ) { 52 : cachedAdaptiveClass = clazz; 53 : } else if (!cachedAdaptiveClass.equals(clazz)) { 54 : throw new IllegalStateException("More than 1 adaptive class found: " 55 : + cachedAdaptiveClass.getClass().getName() 56 : + ", " + clazz.getClass().getName()); 57 : } 58 : } else { 59 : 60 : try { 61 : clazz.getConstructor(type); 62 : Set<Class<?>> wrappers = cachedWrapperClasses; 63 : if (wrappers == null ) { 64 : cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); 65 : wrappers = cachedWrapperClasses; 66 : } 67 : wrappers.add(clazz); 68 : 69 : } catch (NoSuchMethodException e) { 70 : clazz.getConstructor(); 71 : 72 : if (name == null || name.length() == 0 ) { 73 : name = findAnnotationName(clazz); 74 : if (name == null || name.length() == 0 ) { 75 : if (clazz.getSimpleName().length() > type.getSimpleName().length() 76 : && clazz.getSimpleName().endsWith(type.getSimpleName())) { 77 : name = clazz.getSimpleName().substring(0 , clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); 78 : } else { 79 : throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); 80 : } 81 : } 82 : } 83 : 84 : String[] names = NAME_SEPARATOR.split(name); 85 : if (names != null && names.length > 0 ) { 86 : 87 : Activate activate = clazz.getAnnotation(Activate.class); 88 : if (activate != null ) { 89 : cachedActivates.put(names[0 ], activate); 90 : } 91 : for (String n : names) { 92 : 93 : if (!cachedNames.containsKey(clazz)) { 94 : cachedNames.put(clazz, n); 95 : } 96 : 97 : Class<?> c = extensionClasses.get(n); 98 : if (c == null ) { 99 : extensionClasses.put(n, clazz); 100 : } else if (c != clazz) {101 : throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());102 : }103 : }104 : }105 : }106 : }107 : }108 : } catch (Throwable t) {109 : 110 : IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);111 : exceptions.put(line, e);112 : }113 : }114 : } 115 : } finally {116 : reader.close();117 : }118 : } catch (Throwable t) {119 : logger.error("Exception when load extension class(interface: " +120 : type + ", class file: " + url + ") in " + url, t);121 : }122 : } 123 : }124 : } catch (Throwable t) {125 : logger.error("Exception when load extension class(interface: " +126 : type + ", description file: " + fileName + ")." , t);127 : }128 : }
第 9 行:获得完整的文件名( 相对路径 )。例如:"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory"
。
第 12 至 18 行:获得文件名对应的所有文件 URL 数组。例如:ExtensionFactory 的配置文件
第 21 至 24 行:逐个文件 URL 遍历。
第 27 行:逐行 遍历。
第 29 至 32 行:跳过当前被 "#"
注释掉的情况,例如 #spring=xxxxxxxxx
。
第 34 至 40 行:按照 key=value
的配置拆分。其中 name
为拓展名,line
为拓展实现类名。注意 ,上文我们提到过 Dubbo SPI 会兼容 Java SPI 的配置格式,那么按照此处的解析方式,name
会为空。这种情况下,拓展名会自动生成,详细见第 71 至 82 行的代码。
第 42 至 48 行:判断拓展实现类,需要实现拓展接口。
第 50 至 57 行:缓存自适应拓展对象的类到 cachedAdaptiveClass
属性。在 「6. @Adaptive」 详细解析。
第 59 至 67 行:缓存拓展 Wrapper 实现类到 cachedWrapperClasses
属性。
第 69 至 105 行:若获得构造方法失败,则代表是普通的拓展实现类,缓存到 extensionClasses
变量 中。
第 70 行:调用 Class#getConstructor(Class<?>... parameterTypes)
方法,获得参数为空的构造方法。
第 72 至 82 行:未配置拓展名,自动生成。适用于 Java SPI 的配置方式 。例如,xxx.yyy.DemoFilter 生成的拓展名为 demo
。
第 73 行:通过 @Extension
注解的方式设置拓展名的方式已经废弃 ,胖友可以无视该方法。
第 84 行:获得拓展名。使用逗号进行分割,即多个拓展名可以对应同一个拓展实现类。
第 86 至 90 行:缓存 @Activate
到 cachedActivates
。在 「7. @Activate」 详细解析。
第 93 至 95 行:缓存到 cachedNames
属性。
第 96 至 102 行:缓存拓展实现类到 extensionClasses
变量。注意 ,相同拓展名,不能对应多个不同的拓展实现。
第 108 至 112 行:若发生异常,记录到异常集合 exceptions
属性。
4.2.4 其他方法 如下方法,和该流程无关,胖友可自行查看。
4.3 获得拓展加载器 在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.3.1 getExtensionLoader #getExtensionLoader(type)
静态 方法,根据拓展点的接口,获得拓展加载器。代码如下:
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); 1 : 8 : @SuppressWarnings ("unchecked" ) 9 : public static <T> ExtensionLoader<T> getExtensionLoader (Class<T> type) { 10 : if (type == null ) 11 : throw new IllegalArgumentException("Extension type == null" ); 12 : 13 : if (!type.isInterface()) { 14 : throw new IllegalArgumentException("Extension type(" + type + ") is not interface!" ); 15 : } 16 : 17 : if (!withExtensionAnnotation(type)) { 18 : throw new IllegalArgumentException("Extension type(" + type + 19 : ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!" ); 20 : } 21 : 22 : 23 : ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 24 : if (loader == null ) { 25 : EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 26 : loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 27 : } 28 : }
第 12 至 15 行:必须是接口。
第 16 至 20 行:调用 #withExtensionAnnotation()
方法,校验必须使用 @SPI
注解标记。
第 22 至 27 行:从 EXTENSION_LOADERS
静态 中获取拓展接口对应的 ExtensionLoader 对象。若不存在,则创建 ExtensionLoader 对象,并添加到 EXTENSION_LOADERS
。
4.3.2 构造方法 构造方法,代码如下:
private final Class<?> type;private final ExtensionFactory objectFactory; 1 : private ExtensionLoader (Class<?> type) { 2 : this .type = type; 3 : objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); 4 : }
objectFactory
属性,对象工厂,功能上和 Spring IOC 一致 。
4.4 获得指定拓展对象 在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
4.4.1 getExtension #getExtension()
方法,返回指定名字的扩展对象。如果指定名字的扩展不存在,则抛异常 IllegalStateException 。代码如下:
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); 1 : 5 : 11 : @SuppressWarnings ("unchecked" ) 12 : public T getExtension (String name) { 13 : if (name == null || name.length() == 0 ) 14 : throw new IllegalArgumentException("Extension name == null" ); 15 : 16 : if ("true" .equals(name)) { 17 : return getDefaultExtension(); 18 : } 19 : 20 : Holder<Object> holder = cachedInstances.get(name); 21 : if (holder == null ) { 22 : cachedInstances.putIfAbsent(name, new Holder<Object>()); 23 : holder = cachedInstances.get(name); 24 : } 25 : Object instance = holder.get(); 26 : if (instance == null ) { 27 : synchronized (holder) { 28 : instance = holder.get(); 29 : 30 : if (instance == null ) { 31 : instance = createExtension(name); 32 : 33 : holder.set(instance); 34 : } 35 : } 36 : } 37 : return (T) instance; 38 : }
第 15 至 18 行:调用 #getDefaultExtension()
方法,查询默认的 拓展对象。在该方法的实现代码中,简化代码为 getExtension(cachedDefaultName);
。
第 19 至 28 行:从缓存中,获得拓展对象。
第 29 至 31 行:当缓存不存在时,调用 #createExtension(name)
方法,创建拓展对象。
第 33 行:添加创建的拓展对象,到缓存中。
4.4.2 createExtension #createExtension(name)
方法,创建拓展名的拓展对象,并缓存。代码如下:
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); 1 : 7 : @SuppressWarnings ("unchecked" ) 8 : private T createExtension (String name) { 9 : 10 : Class<?> clazz = getExtensionClasses().get(name); 11 : if (clazz == null ) { 12 : throw findException(name); 13 : } 14 : try { 15 : 16 : T instance = (T) EXTENSION_INSTANCES.get(clazz); 17 : if (instance == null ) { 18 : 19 : EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 20 : instance = (T) EXTENSION_INSTANCES.get(clazz); 21 : } 22 : 23 : injectExtension(instance); 24 : 25 : Set<Class<?>> wrapperClasses = cachedWrapperClasses; 26 : if (wrapperClasses != null && !wrapperClasses.isEmpty()) { 27 : for (Class<?> wrapperClass : wrapperClasses) { 28 : instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 29 : } 30 : } 31 : return instance; 32 : } catch (Throwable t) { 33 : throw new IllegalStateException("Extension instance(name: " + name + ", class: " + 34 : type + ") could not be instantiated: " + t.getMessage(), t); 35 : } 36 : }
4.4.3 injectExtension #injectExtension(instance)
方法,注入依赖的属性。代码如下:
1 : 7 : private T injectExtension (T instance) { 8 : try { 9 : if (objectFactory != null ) { 10 : for (Method method : instance.getClass().getMethods()) {11 : if (method.getName().startsWith("set" )12 : && method.getParameterTypes().length == 1 13 : && Modifier.isPublic(method.getModifiers())) { 14 : 15 : Class<?> pt = method.getParameterTypes()[0 ];16 : try {17 : 18 : String property = method.getName().length() > 3 ? method.getName().substring(3 , 4 ).toLowerCase() + method.getName().substring(4 ) : "" ;19 : 20 : Object object = objectFactory.getExtension(pt, property);21 : 22 : if (object != null ) {23 : method.invoke(instance, object);24 : }25 : } catch (Exception e) {26 : logger.error("fail to inject via method " + method.getName()27 : + " of interface " + type.getName() + ": " + e.getMessage(), e);28 : }29 : }30 : }31 : }32 : } catch (Exception e) {33 : logger.error(e.getMessage(), e);34 : }35 : return instance;36 : }
第 9 行:必须有 objectFactory
属性,即 ExtensionFactory 的拓展对象,不需要注入依赖的属性。
第 10 至 13 行:反射获得所有的方法,仅仅处理 public setting
方法。
第 15 行:获得属性的类型。
第 18 行:获得属性名。
第 20 行:获得属性值 。注意 ,此处虽然调用的是 ExtensionFactory#getExtension(type, name)
方法,实际获取的不仅仅是拓展对象,也可以是 Spring Bean 对象。答案在 「8. ExtensionFactory」 揭晓。
第 21 至 24 行:设置属性值。
4.4.4 其他方法 如下方法,和该流程无关,胖友可自行查看。
4.5 获得自适应的拓展对象 在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()
友情提示,胖友先看下 「6. Adaptive」 的内容,在回到此处。
4.5.1 getAdaptiveExtension #getAdaptiveExtension()
方法,获得自适应拓展对象。
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();private volatile Throwable createAdaptiveInstanceError; 1 : 6 : @SuppressWarnings ("unchecked" ) 7 : public T getAdaptiveExtension () { 8 : 9 : Object instance = cachedAdaptiveInstance.get(); 10 : if (instance == null ) { 11 : 12 : if (createAdaptiveInstanceError == null ) { 13 : synchronized (cachedAdaptiveInstance) { 14 : instance = cachedAdaptiveInstance.get(); 15 : if (instance == null ) { 16 : try { 17 : 18 : instance = createAdaptiveExtension(); 19 : 20 : cachedAdaptiveInstance.set(instance); 21 : } catch (Throwable t) { 22 : 23 : createAdaptiveInstanceError = t; 24 : throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); 25 : } 26 : } 27 : } 28 : 29 : } else { 30 : throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); 31 : } 32 : } 33 : return (T) instance; 34 : }
第 9 行:从缓存 cachedAdaptiveInstance
属性中,获得自适应拓展对象。
第 28 至 30 行:若之前创建报错,则抛出异常 IllegalStateException 。
第 14 至 20 行:当缓存不存在时,调用 #createAdaptiveExtension()
方法,创建自适应拓展对象,并添加到 cachedAdaptiveInstance
中。
第 22 至 24 行:若创建发生异常,记录异常到 createAdaptiveInstanceError
,并抛出异常 IllegalStateException 。
4.5.2 createAdaptiveExtension #createAdaptiveExtension()
方法,创建自适应拓展对象。代码如下:
@SuppressWarnings ("unchecked" )private T createAdaptiveExtension () { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
调用 #getAdaptiveExtensionClass()
方法,获得自适应拓展类。
调用 Class#newInstance()
方法,创建自适应拓展对象。
调用 #injectExtension(instance)
方法,向创建的自适应拓展对象,注入依赖的属性。
4.5.3 getAdaptiveExtensionClass #getAdaptiveExtensionClass()
方法,获得自适应拓展类。代码如下:
1 : 4 : private Class<?> getAdaptiveExtensionClass() { 5 : getExtensionClasses(); 6 : if (cachedAdaptiveClass != null ) { 7 : return cachedAdaptiveClass; 8 : } 9 : return cachedAdaptiveClass = createAdaptiveExtensionClass(); 10 : }
【@Adaptive
的第一种】第 6 至 8 行:若 cachedAdaptiveClass
已存在,直接返回。的第一种情况。
【@Adaptive
的第二种】第 9 行:调用 #createAdaptiveExtensionClass()
方法,自动生成 自适应拓展的代码实现,并编译 后返回该类。
4.5.4 createAdaptiveExtensionClassCode #createAdaptiveExtensionClassCode()
方法,自动生成自适应拓展的代码实现,并编译后返回该类。
1 : 6 : private Class<?> createAdaptiveExtensionClass() { 7 : 8 : String code = createAdaptiveExtensionClassCode(); 9 : 10 : ClassLoader classLoader = findClassLoader();11 : com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();12 : return compiler.compile(code, classLoader);13 : }
第 8 行:调用 #createAdaptiveExtensionClassCode
方法,自动生成自适应拓展的代码实现的字符串。
🙂 代码比较简单,已经添加详细注释,胖友点击查看。
如下是 ProxyFactory 的自适应拓展的代码实现的字符串生成例子 自适应拓展的代码实现的字符串生成例子
第 9 至 12 行:使用 Dubbo SPI 加载 Compier 拓展接口对应的拓展实现对象,后调用 Compiler#compile(code, classLoader)
方法,进行编译。🙂 因为不是本文的重点,后续另开文章分享。
4.6 获得激活的拓展对象数组 在 Dubbo 的代码里,看到使用代码如下:
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
4.6.1 getExtensionLoader #getExtensionLoader(url, key, group)
方法,获得符合自动激活条件的拓展对象数组。
1 : 14 : public List<T> getActivateExtension (URL url, String key, String group) {15 : 16 : String value = url.getParameter(key);17 : 18 : return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);19 : }20 : 21 : 32 : public List<T> getActivateExtension (URL url, String[] values, String group) {33 : List<T> exts = new ArrayList<T>();34 : List<String> names = values == null ? new ArrayList<String>(0 ) : Arrays.asList(values);35 : 36 : 37 : if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {38 : 39 : getExtensionClasses();40 : 41 : for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {42 : String name = entry.getKey();43 : Activate activate = entry.getValue();44 : if (isMatchGroup(group, activate.group())) { 45 : 46 : T ext = getExtension(name);47 : if (!names.contains(name) 48 : && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) 49 : && isActive(activate, url)) { 50 : exts.add(ext);51 : }52 : }53 : }54 : 55 : Collections.sort(exts, ActivateComparator.COMPARATOR);56 : }57 : 58 : List<T> usrs = new ArrayList<T>();59 : for (int i = 0 ; i < names.size(); i++) {60 : String name = names.get(i);61 : if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { 62 : 63 : if (Constants.DEFAULT_KEY.equals(name)) {64 : if (!usrs.isEmpty()) {65 : exts.addAll(0 , usrs);66 : usrs.clear();67 : }68 : } else {69 : 70 : T ext = getExtension(name);71 : usrs.add(ext);72 : }73 : }74 : }75 : 76 : if (!usrs.isEmpty()) {77 : exts.addAll(usrs);78 : }79 : return exts;80 : }
第 16 行:从 Dubbo URL 获得参数值。例如说,若 XML 配置 Service <dubbo:service filter="demo, demo2" />
,并且在获得 Filter 自动激活拓展时,此处就能解析到 value=demo,demo2
。另外,value
可以根据逗号 拆分。
第 18 行:调用 #getActivateExtension(url, values, group)
方法,获得符合自动激活条件的拓展对象数组。
第 35 至 56 行:处理自动激活的拓展对象们。
第 57 至 74 行:处理自定义配置的拓展对象们。
第 75 至 78 行:将 usrs
合并到 exts
尾部 。
🙂 代码比较简单,胖友直接看注释。
4.6.2 ActivateComparator com.alibaba.dubbo.common.extension.support.ActivateComparator
,自动激活拓展对象排序器。
5. @SPI com.alibaba.dubbo.common.extension.@SPI
,扩展点接口的标识。代码如下:
@Documented @Retention (RetentionPolicy.RUNTIME)@Target ({ElementType.TYPE})public @interface SPI { String value () default "" ; }
6. @Adaptive com.alibaba.dubbo.common.extension.@Adaptive
,自适应拓展信息的标记。代码如下:
@Documented @Retention (RetentionPolicy.RUNTIME)@Target ({ElementType.TYPE, ElementType.METHOD})public @interface Adaptive { String[] value() default {}; }
@Adaptive
注解,可添加类 或方法 上,分别代表了两种不同的使用方式。
友情提示:一个拓展接口,有且仅有一个 Adaptive 拓展实现类。
第一种,标记在类 上,代表手动实现 它是一个拓展接口的 Adaptive 拓展实现类。目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory 有这么用。详细解析见 「8.1 AdaptiveExtensionFactory」 。
第二种,标记在拓展接口的方法 上,代表自动生成代码实现 该接口的 Adaptive 拓展实现类。
7. @Activate com.alibaba.dubbo.common.extension.@Activate
,自动激活条件的标记。代码如下:
@Documented @Retention (RetentionPolicy.RUNTIME)@Target ({ElementType.TYPE, ElementType.METHOD})public @interface Activate { String[] group() default {}; String[] value() default {}; String[] before() default {}; String[] after() default {}; int order () default 0 ; }
对于可以被框架中自动激活加载扩展,@Activate
用于配置扩展被自动激活加载条件。比如,Filter 扩展,有多个实现,使用 @Activate
的扩展可以根据条件 被自动加载。
🙂 分成过滤条件和排序信息两类属性 ,胖友看下代码里的注释。
在 「4.6 获得激活的拓展对象数组」 详细解析。
8. ExtensionFactory com.alibaba.dubbo.common.extension.ExtensionFactory
,拓展工厂接口。代码如下:
@SPI public interface ExtensionFactory { <T> T getExtension (Class<T> type, String name) ; }
ExtensionFactory 自身也是拓展接口,基于 Dubbo SPI 加载具体拓展实现类。
#getExtension(type, name)
方法,在 「4.4.3 injectExtension」 中,获得拓展对象,向创建的拓展对象注入依赖属性 。在实际代码中,我们可以看到不仅仅获得的是拓展对象,也可以是 Spring 中的 Bean 对象。
ExtensionFactory 子类类图如下:ExtensionFactory 类图
8.1 AdaptiveExtensionFactory com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
,自适应 ExtensionFactory 拓展实现类。代码如下:
1 : @Adaptive 2 : public class AdaptiveExtensionFactory implements ExtensionFactory { 3 : 4 : 7 : private final List<ExtensionFactory> factories; 8 : 9 : public AdaptiveExtensionFactory () { 10 : 11 : ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);12 : List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();13 : for (String name : loader.getSupportedExtensions()) {14 : list.add(loader.getExtension(name));15 : }16 : factories = Collections.unmodifiableList(list);17 : }18 : 19 : public <T> T getExtension (Class<T> type, String name) {20 : 21 : for (ExtensionFactory factory : factories) {22 : T extension = factory.getExtension(type, name);23 : if (extension != null ) {24 : return extension;25 : }26 : }27 : return null ;28 : }29 : 30 : }
@Adaptive
注解,为 ExtensionFactory 的自适应 拓展实现类。
构造 方法,使用 ExtensionLoader 加载 ExtensionFactory 拓展对象的实现类。若胖友没自己实现 ExtensionFactory 的情况下,factories
为 SpiExtensionFactory 和 SpringExtensionFactory 。
#getExtension(type, name)
方法,遍历 factories
,调用其 #getExtension(type, name)
方法,直到获得到属性值。
8.2 SpiExtensionFactory com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
,SPI ExtensionFactory 拓展实现类。代码如下:
public class SpiExtensionFactory implements ExtensionFactory { 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 com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
,Spring ExtensionFactory 拓展实现类。代码如下:
public class SpringExtensionFactory implements ExtensionFactory { private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext (ApplicationContext context) { contexts.add(context); } public static void removeApplicationContext (ApplicationContext context) { contexts.remove(context); } @Override @SuppressWarnings ("unchecked" ) public <T> T getExtension (Class<T> type, String name) { for (ApplicationContext context : contexts) { if (context.containsBean(name)) { Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } return null ; } }
#getExtension(type, name)
方法,遍历 contexts
,调用其 ApplicationContext#getBean(name)
方法,获得 Bean 对象,直到成功并且值类型正确。
8.3.1 例子 DemoFilter 是笔者实现的 Filter 拓展实现类,代码如下:
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 ; } }
666. 彩蛋 知识星球
比想象中的长的多的多。初始理解会比较辛苦,梳理干净后实际很简单。
能够耐心到此处的胖友,为你点赞。👍👍👍