SPI 全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
JAVA SPI 机制本文不做讲解,自行了解。
SEATA SPI
Seata 的 SPI 机制由类 EnhancedServiceLoader 提供 , 翻译一下就是: 增强服务载入器
Seata 的SPI 是一个基础模块 ,对于Seata框架起着至关重要的作用, 熟悉了 Java SPI 机制后,Seata 就更容易理解,
只是相对于Java SPI 做了一些增强, 例如可以通过名字,可以存在构造参数
大致上可以分为以下三个维度加载需要的服务
- load 通过 Class 载入一个默认服务
- load 通过 Class 和名字载入一个服务
- loadAll 通过 Class 载入一组服务
Seata SPI 兼容了 JAVA SPI, 首先从目录 META-INF/services/ 加载服务,然后在从目录 META-INF/seata/ 加载服务。
虽然这么说,但是 seata 默认的扩展都还是放在目录 META-INF/services/ 下面。例如: Seata序列化扩展的位置:
META-INF/services/io.seata.core.serializer.Serializer, 内容: io.seata.serializer.seata.SeataSerializer
Seata SPI的每个扩展类都是有名字的,但是不放在配置文件中,而是在实现类上面会有注解 @LoadLevel,例如:
@LoadLevel(name = "SEATA")
public class SeataSerializer implements Serializer {
}
Seata SPI 机制和 Dubbo 的 SPI 非常相似, 这是对 Dubbo SPI 的一种肯定。
IOC部分简化了, Holder模式, 单例加载的双重锁都是值得学习的地方。
载入级别
LoadLevel 的声明如下:
public @interface LoadLevel {
/**
* 名字, 需要唯一
*/
String name();
/**
* Order int.
* loadAll 方法需要这个字段的值,对加载到的 ExtensionDefinition 进行排序
* 对于需要获得多个实现进行排序的场景,通常用于责任链模式。
* 在Seata 中, 在拦截器 StateHandlerInterceptor 的实现中 Saga 拦截器将优先于其他的拦截器
*/
int order() default 0;
/**
* Scope enum. 可以选择单例(SINGLETON),原型(PROTOTYPE)
*/
Scope scope() default Scope.SINGLETON;
}
通过Class加载一个服务
这是通过指定的接口加载默认实现。 默认值为: 排在最后面的实现。
public static <S> S load(Class<S> service, ClassLoader loader) throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(loader);
}
public static <S> S load(Class<S> service) throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(findClassLoader());
}
通过Class和名字加载一个服务
首先会加载指定接口的所有Class,
然后根据名字加载具体实现的实例
// 通过类 EnhancedServiceLoader 的 ClassLoader 载入
public static <S> S load(Class<S> service, String activateName) throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, findClassLoader());
}
// 外部可以指定ClassLoader的载入
public static <S> S load(Class<S> service, String activateName, ClassLoader loader)
throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, loader);
}
// 特殊支持, 支持构造参数初始化
public static <S> S load(Class<S> service, String activateName, Object[] args)
throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, args, findClassLoader());
}
// 特殊支持, 支持构造参数初始化, 可以指定参数类型
public static <S> S load(Class<S> service, String activateName, Class[] argsType, Object[] args)
throws EnhancedServiceNotFoundException {
return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, argsType, args, findClassLoader());
}
通过 Class 载入一组服务
// 通过指定接口,载入所有实例
public static <S> List<S> loadAll(Class<S> service) {
return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(findClassLoader());
}
// 通过指定接口和构造参数信息, 载入所有实例
public static <S> List<S> loadAll(Class<S> service, Class[] argsType, Object[] args) {
return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(argsType, args, findClassLoader());
}
InnerEnhancedServiceLoader
内部增强服务载入器, 前面载入的方法都是通过 InnerEnhancedServiceLoader 类实现的。
那么 InnerEnhancedServiceLoader 有什么用? 为什么要存在呢?
InnerEnhancedServiceLoader 的作用:
- 提供了 Class 到 InnerEnhancedServiceLoader 的缓存
- 提供了 Class 到 ExtensionDefinition 的缓存
- 提供了 扩展名 到 ExtensionDefinition 的缓存
- 提供了 ExtensionDefinition 到 接口实例 的缓存
总体来看, 它避免了多次载入同一个接口的扩展时, 不必要的载入。
然而它还是一个小型的IOC, 在创建对象的时候支持构造函数注入,
获取实例时, 如果是PROTOTYPE类型的, 每次获取都会创建新的实例。
而且还支持初始化, 对于实现了接口 Initialize 的实现类, 创建对象完成后, 会调用实例的 init() 方法。