Dubbo-SPI系列文章目录
Dubbo-SPI(一)-加载机制概述
Dubbo-SPI(二)-@SPI注解
Dubbo-SPI(三)-getExtension实现原理
Dubbo-SPI(四)-@Adaptive注解
Dubbo-SPI(五)-@Activate注解
文章目录
Java SPI(Service Provider Interface)
Java SPI 使用了 策略模式,一个接口 多种实现
只需要声明接口
具体的实现并不在程序中 直接确定
而是由程序之外的配置掌控,用于具体实现的装配
步骤如下:
- 定义一个接口 及 对应方法
- 对接口进行实现
- 在META/service/ 目录下,创建一个 以 接口全路径 命名的文件
- 文件内中 为 具体实现类的 全路径名,如果有多个,用分行符 分隔
- 在代码中 通过 java.util.ServiceLoader 来加载 具体的 实现类
Dubbo SPI的改进
- Java SPI 会 一次性 实例化 扩展点的所有实现,扩展实现可能很耗时,也可能用不上,会浪费资源
- 如果扩展加载失败,连扩展的名称都获取不到,异常信息也可能会被 “吞掉”,问题追踪比较困难;Dubbo SPI 在扩展 加载失败的时候 会 先抛出 真实异常 并 打印日志,扩展点 在 被动加载的时候,即使 有部分扩展加载失败 也不会影响 其他扩展点和整个框架的使用
- Dubbo SPI 增加了 对扩展的 IOC 和 AOP 支持,一个扩展 可以 直接setter注入 其他扩展
- Dubbo SPI 只是加载 配置文件中的类,并 分成 不同种类的缓存 放在内存中,而不会 立即 全部 初始化,在性能上会有更好的表现
- Dubbo SPI 支持 包装 扩展类,推荐 把通用的抽象逻辑 放在 包装类中,用于实现扩展点的AOP特性。这和Spring的动态代理 思想一样,在被代理类 的 前后 插入自己的逻辑 进行增强
扩展点配置规范
规范名 | 规范说明 |
---|---|
SPI配置文件路径 | META-INF/services(兼容Java SPI)、META-INF/dubbo/(用户扩展)、META-INF/dubbo/internal (Dubbo内部使用) |
SPI配置文件名称 | 全路径类名 |
文件内容格式 | key=value,多个 用 换行符 分隔 |
扩展点的分类与缓存
- Class缓存:Dubbo SPI 获取 扩展类时,会先从缓存中读取。如果缓存中不存在,则 加载配置文件,根据配置 把Class 缓存到内存中,并不会直接 全部初始化
- 实例缓存:缓存 实例化后的对象,每次获取的时候,会先从缓存中读取,如果缓存中读取不到,则重新加载 并 缓存起来,按需实例化并缓存
被缓存的Class和对象实例 可以根据不同的特性 分别不同的类别
- 普通扩展类:最基础的,配置在SPI配置文件中的 扩展类实现
- 包装扩展类:这种Wrapper(包装)类 没有具体的实现逻辑,只是做了通过逻辑的抽象,并且 需要 在构造方法中 传入 一个 具体的 扩展接口的实现
- 自适应扩展类:一个扩展接口 会有 多种实现类,具体使用哪个,可以不写死在配置 或者 代码中,在运行时,通过传入URL对象中的 某些参数parameter来动态确定,相关注解是@Adaptive
- 其他缓存:扩展类加载器缓存、扩展名缓存
Class缓存
集合名 | 缓存类型 |
---|---|
Holder<Map<String,Class<?>>> cachedClasses | 普通扩展类缓存,不包括自适应扩展类 和 包装扩展类 |
Set<Class<?>> cachedWrapperClasses | 包装扩展类缓存 |
Class<?> cachedAdaptiveClass | 自适应扩展类缓存,只能同时存在一个 |
Map<Stirng, Activate> cachedActivates | 扩展名 与 @Activate(自动激活)的缓存 |
ConcurrendMap<Class<?>, ExtensionLoader<?>> EXTESION_LOADERS | 扩展类 与 对应的类加载器缓存 |
ConcurrendMap<Class<?>, String> cachedNames | 扩展类 与 扩展名缓存 |
实例缓存
集合名 | 缓存类型 |
---|---|
Holder<Obejct> cachedAdaptiveInstance | 实例化后的 自适应扩展类的 对象,只能同时存在一个 |
ConcurrendMap<Sting, Holder<Object>> cachedInstances | 扩展名 与 扩展对象实例缓存 |
ConcurrendMap<Class<?>, Object> EXTESION_INSTANCES | 扩展类 与 类初始化后的实例 |
扩展点的特性
自动包装
ExtensionLoader在加载扩展时,如果发现 这个扩展类 包含 其他扩展点 作为 构造函数的参数,则这个扩展类 就会被认为是 封装类
自动加载
如果某个扩展类 是 另外一个 扩展类的成员属性,并且拥有setter方法,那么 框架也会 自动注入 对应的 扩展点实例。
自适应-Adaptive
使用@Adaptive注解,可以 动态地 通过URL中的参数 来确定 要使用哪一个具体的实现类,从而解决 自动加载中的 实例注入问题
自动激活-Activate
使用@Activate注解,可以标记 对应的扩展点 默认被激活启用,这个注解 还可以通过 传入不同的参数,设置 扩展点 在不同的条件下 被 自动激活
主要使用场景 是 某个扩展点 的 多个实现类 需要同时启用