- Dubbo 采用 Microkernel + Plugin 模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
- 采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息。
来源
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
JDK 标准的 SPI
详细请参考:Java 中 SPI 机制深入及源码解析
-
主要类:
ServiceLoader
默认从META-INF/services/
路径下读取文件 -
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源
-
如果扩展点加载失败,连扩展点的名称都拿不到了
部分核心代码
// 判断是否有下一个扩展实现 private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); // 根据全路径获取配置的扩展实现 if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; } // 获得下一个扩展实现 private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { // 根据全限定名加载类 c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { // 判断是否是 SPI 接口的实现,如果是则加入到提供者列表 S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
DUBBO 加强的 SPI
约定
在扩展类的 jar 包内,放置扩展点配置文件 META-INF/dubbo/
接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
示例
以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
,内容为:
xxx=com.alibaba.xxx.XxxProtocol
实现类内容 :
package com.alibaba.xxx;
import com.alibaba.dubbo.rpc.Protocol;
public class XxxProtocol implements Protocol {
// ...
}
配置模块中的配置
Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现。比如:
<dubbo:protocol name="xxx" />
示例:https://www.jianshu.com/p/dc616814ce98
https://www.jianshu.com/p/bc523348f519
源码分析
@SPI 、@Adaptive 、@Activate 作用
- @SPI (注解在类上) : @SPI 注解标识了接口是一个扩展点 , 属性 value 用来指定默认适配扩展点的名称。
- @Activate (注解在类型和方法上) : @Activate 注解在扩展点的实现类上 ,表示了一个扩展类被获取到的的条件,符合条件就被获取,不符合条件就不获取 ,根据 @Activate 中的 group 、 value 属性来过滤 。具体参考 ExtensionLoader 中的 getActivateExtension 函数。 可参考 Dubbo SPI 之 Activate 详解
- @Adaptive (注解在类型和方法上) :
- 注解在类上 , 这个类就是缺省的适配扩展。
- 注解在接口的方法上,ExtensionLoader 根据接口定义动态的生成适配器代码,并实例化这个生成的动态类。
- 可参考 Dubbo SPI 之 Adaptive 详解
核心类 ExtensionLoader
类似与 Java 的 ServiceLoader
最简单使用例子:
ExtensionLoader.getExtensionLoader(CompatibleExt.class).getExtension("impl1")
第一步 getExtensionLoader
根据 SPI 接口创建出一个 ExtensionLoader 实例,如果本地缓存中已存在则使用缓存的,如果不存在则实例化一个,与接口相关联放入缓存。
// 每一个 SPI 扩展有一个对应的加载类 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!"); } // 没有使用 @SPI 做注解的接口 if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException ... } // 从本地缓存中加载指定 SPI 的加载类 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; } // 构造方法 private ExtensionLoader(Class<?> type) { this.type = type; // objectFactory是一个 ExtensionFactory 类型的属性,主要用于加载需要注入的类型的实现 // objectFactory 主要用在注入那一步,详细说明见注入时候的说明 // getAdaptiveExtension 方法获取一个运行时自适应的扩展类型 详细介绍会在下文 // 这里记住非 ExtensionFactory 类型的返回的都是一个AdaptiveExtensionFactory objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
第二步 getExtension
根据名称获得扩展实现
// 根据名称获得扩展实现 public T getExtension(String name) { if (name == null || name.length() == 0) throw new IllegalArgumentException("Extension name == null"); // 如果 名称是 true ,则获得默认的扩展实现 if ("true".equals(name)) { // 如果 @SPI 指定了默认扩展名 则设置 cachedDefaultName // cachedClasses 存放 指定路径下名称对应的扩展实现 // 如果 cachedDefaultName 没有设置,则返回 null return getDefaultExtension(); } // 从本地缓存中取一个 实例 // Holder 在多线程中 防并发 Holder<Object> holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<Object>()); holder = cachedInstances.get(name); } // 容器中 取实例 如果存在则返回 不存在则加载创建 // 使用二次校验 Holder 中使用了 volatile 防止指令重排引起的并发问题 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
// 创建扩展实例 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, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 注入 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { // 使用包装类包装实例 进行注入 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException ... } }
getExtensionClasses
的实现是为了加载指定路径的文件配置
private Map<String, Class<?>> getExtensionClasses() { Map<String, Class<?>> classes = cachedClasses.get(); // 如果本地 缓存中不存在 则初始化 cachedClasses // 同样使用的是二次校验实例化 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } // 加载 SPI 接口 ,获得配置中的 实现 private Map<String, Class<?>> loadExtensionClasses() { ... // 设置 @ SPI 指定的默认名称 if (names.length == 1) cachedDefaultName = names[0]; ... // 加载目录 下的约定文件内容 META-INF/services/ 、 META-INF/dubbo/ 、 META-INF/dubbo/internal/ // loadDirectory --> loadResource --> loadClass Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); ... return extensionClasses; } // 加载类 private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // 判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口 // 与 instanceof 不同之处在于 instanceof 第一个参数需要是实例 isAssignableFrom 可以用类判断 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException ... } // 是否 被 @Adaptive 注解 被 @Adaptive 注解的实现类只能有一个,否则抛异常 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException ... } } else // 是否是包装类, 放入本地缓存 if (isWrapperClass(clazz)) { Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } else { clazz.getConstructor(); // 如果配置中没有指定别名,则查看实现类上 @Extension 注解指定的别名 否则抛异常 if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException ... } } // 多个别名分割 String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); // 如果含有 @Activate 注解,则放入本地缓存中 if (activate != null) { cachedActivates.put(names[0], activate); } else { ... } for (String n : names) { // 根据实现类找别名 只能存在一个 if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class<?> c = extensionClasses.get(n); if (c == null) { extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException ... } } } } }
injectExtension
根据 set 方法注入
private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { // 必须是 set 方法 if (method.getName().startsWith("set") // 方法必须只有一个参数 && method.getParameterTypes().length == 1 // set 方法的修饰符 必须是 public && 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; }
objectFactory.getExtension
中 objectFactory
是私有构造方法中实例化的
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
具体的方法内容分析,在下一篇
至此关于简单使用分析完毕。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于