SPI全程Service Provider Interface,也就是服务发现机制。SPI分离服务接口和具体实现类,解耦服务调用方和实现者,提升应用框架可扩展性,实现可插拔微内核架构。SPI遵循基于接口编程而非具体实现,满足面向对象设计依赖倒置原则。同时,还支持开闭原则,也就是对扩展开放,对修改封闭。
在Java中,很多框架还使用SPI机制,如Spring框架、数据库驱动和日志接口,另外Dubbo还提供更高级SPI扩展机制。
01 Java SPI
1.1 Java SPI原理

Jdk 1.6引入SPI机制,提供数据库、日志框架和消息服务扩展接口,达到动态加载服务实现。底层原理主要包括四个步骤,比如服务接口、服务提供者、服务配置和服务加载器。
组件 | 描述 |
---|---|
服务接口 | 定义服务抽象功能接口,规定服务提供者需要实现的功能 |
服务提供者 | 服务提供者实现服务接口,提供具体实现。例如,JDBC不同数据库驱动程序(MySQL、PostgreSQL等)提供各自实现类 |
配置文件 | 服务提供者在META-INF/services/ 目录下创建服务接口权限类名文件,然后写入服务提供者实现类的全限定类名到文件 |
服务加载器(ServiceLoader) | 在Java中,提供ServiceLoader 类扫描META-INF/services/ 目录配置文件,找到需要事务配置文件进行加载和实例化服务提供者实现类 |
1.2 Java SPI实现
1.2.1 服务接口
定义服务接口或业务抽象,规定服务提供者需要实现的功能。
java
public interface PaymentService {
void processPayment(double amount);
}
1.2.2 提供者服务实现
服务提供者实现服务接口,提供具体的实现。
java
public class PayPalPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing payment via PayPal: $" + amount);
}
}
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing payment via Credit Card: $" + amount);
}
}
1.2.3 服务配置
接下来,创建 ETA-INF/services/com.feiyu.PaymentService
文件,文件回填服务实现类全限定类名,注意每个服务提供者实现类占据一行。
java
com.feiyu.pay.PayPalPaymentService
com.feiyu.credit.CreditCardPaymentService
1.2.4 服务加载器
在Java中,提供 ServiceLoader
类扫描 META-INF/services/
目录查找接口配置文件,然后动态加载和实例化服务提供者实现类。
java
public class PaymentApp {
public static void main(String[] args) {
// 加载所有PaymentService实现
ServiceLoader<PaymentService> services= ServiceLoader.load(PaymentService.class);
// 遍历所有可用服务实现
for (PaymentService service: services) {
service.processPayment(100.0);
}
}
}
1.5 Java SPI总结
Java SPI底层通过java.util.ServiceLoader#load
进行加载,每次调用重新扫描META-INF/services/
目录服务接口配置文件返回循环迭代器,程序只需要循环调用Class.forName
进行反射查找和实例化服务提供者即可。
通过加载逻辑直观看出存在一些弊端,比如重复加载不添加缓存存在性能开销,一次性加载全部服务提供者存在无效加载,以及不能指定特定服务提供者进行加载。
02 Dubbo SPI演进之路
Java SPI存在性能问题、无效加载和不能指定加载服务提供者,所以为了解决这些问题Dubbo重新设计一套SPI实现机制,而且还提供后置增强实例。Dubbo SPI底层提供指定参数条件、默认或者激活方式获取服务提供者(也即是,所谓的扩展类),实现服务提供者自动加载和切换,是构建可插拔、模块化服务架构的重要工具。
Dubbo SPI组件 | Java SPI组件 |
---|---|
扩展接口 | 服务接口 |
扩展类 | 服务提供者 |
扩展配置 | 服务配置 |
扩展加载器 | 服务加载器 |
03 Dubbo SPI规范
3.1 扩展工厂
Dubbo提供ExtensionFactory进行扩展类实例(setter)依赖注入处理,对扩展类实例进行加强。底层提供三种实现,包括AdaptiveExtensionFactory、SpiExtensionFactory和SpringExtensionFactory三种实现。
需要注意的是,扩展工厂底层是通过Java SPI进行服务发现加载,所以实现配置会全部进行加载。
java
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}
3.2 扩展接口
Dubbo SPI约定服务接口必须满足两个条件,也就是接口类和接口被@SPI
修饰。
java
@SPI
public interface PaymentService {
void processPayment(double amount);
}
3.3 扩展包装器
Dubbo SPI提供包装器进行扩展类加强,格式就是包装器必须提供扩展接口构造函数。另外,可以通过@Wrapper
额外指定需要包装范围,默认不添加都需要包装增强。
java
// 申明方式一
public class WrapperPaymentService implements PaymentService {
private final PaymentService paymentService;
public WrapperPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
@Override
public void processPayment(double amount) {
this.paymentService.processPayment(amount);
}
}
// 申明方式二
@Wrapper
public class WrapperPaymentService implements PaymentService {
private final PaymentService paymentService;
public WrapperPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
@Override
public void processPayment(double amount) {
this.paymentService.processPayment(amount);
}
}
public @interface Wrapper {
// 指定需要包装的扩展名
String[] matches() default {};
// 排除不进行包装的扩展名
String[] mismatches() default {};
}
3.4 配置默认扩展名
Dubbo SPI申明使用@SPI
注解注释接口就是扩展接口,以及通过@SPI(value=?)
注解指定值就是默认扩展名。
java
@SPI(value="pay") // 指定默认扩展名为pay
public interface PaymentService {
void processPayment(double amount);
}
3.5 自适应扩展类
Dubbo SPI机制提供@Adaptive
注解注释自适应扩展类,每个扩展接口仅支持唯一一个自适应扩展类,所以,需要当心选择META-INF/dubbo/internal/
目录是不支持覆盖处理的。
java
@Adaptive
public class PayPalPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing payment via PayPal: $" + amount);
}
}
3.6 激活扩展类
Dubbo SPI提供@Activate
注解用于标注激活扩展类,主要用途在于能通过URL参数模糊匹配到扩展点,然后返回符合条件的激活扩展类。
java
public @interface Activate {
// 激活组
String[] group() default {};
// 激活扩展配置, 例如 Activate(value="key1:value1, key2")
String[] value() default {};
// 排在哪些激活点迁
@Deprecated
String[] before() default {};
// 排在哪些激活点后
@Deprecated
String[] after() default {};
// 顺序
int order() default 0;
}
3.6.1 激活扩展点配置
java
@Activate(group={"mas", "ums"}, value={"key1:value1, key2:value2"})
public class WechatPaymentService implements PaymentService {
@Override
public void processPayment(double amount) {
System.out.println("Processing payment via wechat: $" + amount);
}
}
3.6.2 激活扩展注解匹配机制
激活选项 | 样例 | 内容 |
---|---|---|
group | {"mas", "ums"} |
允许进行匹配的组,为空表示默认匹配,仅支持精确匹配 |
value | {"key1:value1, key2"} |
允许进行匹配的参数值,为空默认匹配,支持后缀匹配和精确匹配 |
接下来,就是细化激活配置与URL参数各种匹配规则。
URL参数 | 激活配置 | 匹配机制 |
---|---|---|
?k1=v1&k2=v2 | @Activate(value={"k1:v1, k2:v2"}) | 精确匹配 |
?k1=v1&k2=v2 | @Activate(value={"k1:v1, k2"}) | 参数匹配,URL对应键参数不能为空 |
?k1=v1&k2=v2 | @Activate(value={"k1:v1, g.k2"}) | 参数后缀匹配,也就是激活配置参数后缀匹配="." + URL参数Key,URL对应Key参数不能为空 |
3.7 普通扩展类
普通扩展类无须通过@Activate
、@Adaptive
注解修饰,也不是包装器扩展类,而且必须仅包含无参构造器。
3.8 扩展实例依赖注入
Dubbo SPI可以通过依赖注入增强扩展实例,主要针对就是setter方法。其中,可以额外指定@Inject
注解设置注入方式和是否注入。另外,也可以通过@DisableInject
禁止当前方法进行依赖注入。
java
public @interface Inject {
// 是否开启注入
boolean enable() default true;
// 注入方式
InjectType type() default ByName;
enum InjectType{
ByName,
ByType
}
}
3.9 扩展初始化
Dubbo SPI可以Lifecycle生命周期管理接口,方便扩展类实现初始化过程。
csharp
public interface Lifecycle {
// 初始化方法
void initialize() throws IllegalStateException;
// 启动方法
void start() throws IllegalStateException;
// 销毁方法
void destroy() throws IllegalStateException;
}
3.10 扩展配置
Dubbo提供META-INF/dubbo/internal/
、META-INF/dubbo/
和META-INF/services/
三个目录配置扩展,扩展配置文件规范为扩展接口全限定类名
,配置文件内容格式为name1,name2,...,=扩展类全限定类名
。同时,配置文件支持包装器配置。
例如,配置文件META-INF/dubbo/internal/com.feiyu.PaymentService
进行扩展类配置,配置内容如下。
properties
credit=com.feiyu.credit.CreditCardPaymentService
pay,pal,paypal=com.feiyu.pay.PayPalPaymentService
com.feiyu.pay.WechatPaymentService
com.feiyu.wrapper.WrapperPaymentService ## 包装类
扩展类名,Dubbo底层支持手动指定 或者框架自动规则解析 两种方式。其中,框架自动规则解析逻辑就是去除扩展接口名转换为小写字母获得,比如com.feiyu.pay.WechatPaymentService
去除PaymentService
得到wechat
。
需要注意,每个扩展接口可以配置<扩展名, 扩展类>
映射,但是扩展名必须保证扩展接口级别唯一,否则就会抛出异常。
04 Dubbo SPI分析
类似Java SPI ServiceLoader功能,Dubbo SPI底层通过ExtensionLoader封装扩展类加载,以及提供指定参数条件、默认或者激活方式获取扩展类路径,所以ExtensionLoader也是源码重点研究对象。
4.1 ExtensionLoader实例化
ExtensionLoader
没有提供公开构造函数,只能通过ExtensionLoader.getExtensionLoader(Class<T> type)
获取ExtensionLoader实例。
java
public class ExtensionLoader<T> {
// 服务接口扩展加载器缓存
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
// 名字分割匹配模式, 格式=n1,n2,...
private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
// 需要加载服务接口类型
private final Class<?> type;
// 扩展加载工厂: 用于自定义实现扩展加载
private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
this.type = type;
// 使用激活ExtensionFactory加载服务提供者
if(type != ExtensionFactory.class) {
objectFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
}
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!");
}
// 仅支持@SPI注解的服务接口
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;
}
private static <T> boolean withExtensionAnnotation(Class<T> type) {
// 接口有添加@SPI注解
return type.isAnnotationPresent(SPI.class);
}
}
public @interface SPI {
// 指定默认扩展名
String value() default "";
}
获取ExtensionLoader实例过程可以看到,扩展接口必须是被@SPI
注解修饰的接口,扩展接口加载器创建和初始化后缓存到ConcurresntHashMap结构。
4.2 基础解析
4.2.1 默认扩展名解析
方法 | 功能 |
---|---|
ExtensionLoader#cacheDefaultExtensionName( ) | 缓存默认扩展名 |
java
public class ExtensionLoader<T> {
// 扩展接口默认扩展名, 通过@SPI(value=?)指定, 仅支持1个默认扩展名
private String cachedDefaultName;
// 缓存默认扩展名
private void cacheDefaultExtensionName() {
// 获取扩展接口@SPI注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation == null) {
return;
}
// @SPI(value=?): value=默认扩展名(仅支持1个配置), 格式=n1,n2,...,
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
// 不能配置多个
if (names.length > 1) {
throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names));
}
if (names.length == 1) {
cachedDefaultName = names[0];
}
}
}
}
4.2.2 初始化配置加载策略
Dubbo SPI申明<font style="color:#74B602;">ExtensionLoader#loadLoadingStrategies</font>
方法读取扩展配置加载策略,底层使用Java SPI机制进行加载策略,然后通过排序后返回。另外,也提供额外方法手动设置加载策略。
java
public class ExtensionLoader<T> {
// 初始化加载策略
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
public static void setLoadingStrategies(LoadingStrategy... strategies) {
if (ArrayUtils.isNotEmpty(strategies)) {
ExtensionLoader.strategies = strategies;
}
}
private static LoadingStrategy[] loadLoadingStrategies() {
// 通过Java SPI加载LoadingStrategy服务提供者
return stream(load(LoadingStrategy.class).spliterator(), false)
.sorted()
.toArray(LoadingStrategy[]::new);
}
}
public interface LoadingStrategy extends Prioritized {
// 扩展目录
String directory();
// 优先扩展加载器
default boolean preferExtensionClassLoader() {
return false;
}
// 排除包
default String[] excludedPackages() {
return null;
}
// 是否覆盖加载
default boolean overridden() {
return false;
}
}
ExtensionLoader提供扩展加载策略支持,底层使用Java SPI扫描和排序这些扩展加载策略,默认实现包括DubboInternalLoadingStrategy
、DubboLoadingStrategy
和ServicesLoadingStrategy
三种实现。
加载策略 | 加载路径 | 优先级 | 是否支持覆盖 |
---|---|---|---|
DubboInternalLoadingStrategy | META-INF/dubbo/internal/ | 最高 | N |
DubboLoadingStrategy | META-INF/dubbo/ | 中等 | Y |
ServicesLoadingStrategy | META-INF/services/ | 最低 | Y |
4.2.3 获取扩展类
ExtensionLoader提供getExtensionClasses
方法获取扩展类,标签缓存起来避免二次加载。
java
public class ExtensionLoader<T> {
// 缓存已加载扩展类, 格式: n1,n2,...=扩展类全限定名
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
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;
}
}
4.2.4 读取扩展配置
ExtensionLoader提供=DubboInternalLoadingStrategy
、DubboLoadingStrategy
和ServicesLoadingStrategy
三种加载扩展策略,分别调用ExtensionLoader#loadDirectory
执行不同目录配置扩展类加载。
java
public class ExtensionLoader<T> {
// 初始化加载策略
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
// 每行解析异常信息
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
// 读取加载策略加载扩展配置
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;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
loadDirectory(extensionClasses, dir, type, false, false);
}
// 读取扩展目录文件加载扩展类
// @param extensionLoaderClassLoaderFirst: 首先选择ExtensionLoader类加载器
private void loadDirectory(Map<String, Class<?>> extensionClasses,
String dir,
String type,
boolean extensionLoaderClassLoaderFirst,
boolean overridden, String... excludedPackages) {
// 文件路径, 比如:META-INF/dubbo/com.feiyu.PaymentService
String fileName = dir + type;
try {
Enumeration<URL> urls = null;
// 获取类加载器
ClassLoader classLoader = findClassLoader();
// 通过ExtensionLoader进行加载
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);
}
}
// 读取扩展配置文件加载扩展类
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) {
// 读取配置, 格式:n1,n2=com.feiyu.credit.CreditCardPaymentService
// 截断行注释内容
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);
}
}
}
4.3 解析扩展配置
4.3.1 解析扩展类
实现扩展类分类解析,比如自适应扩展类、包装器扩展类、激活扩展类和普通扩展类,然后缓存到对应位置方便查找。
java
public class ExtensionLoader<T> {
// 初始化加载策略
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
// 每行解析异常信息
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
// 扩展类名字, 仅缓存第一个配置扩展类名
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
// 加载约定扩展类
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.");
}
// 自适应扩展类: 如果扩展类有被@Adaptive注解
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);
}
}
}
}
// 是否包装类
private boolean isWrapperClass(Class<?> clazz) {
try {
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
private String findAnnotationName(Class<?> clazz) {
// 获取扩展器Extension注解
org.apache.dubbo.common.Extension extension = clazz.getAnnotation(org.apache.dubbo.common.Extension.class);
if (extension != null) {
return extension.value();
}
// 旧逻辑: 通过类名获取, 截取扩展接口后缀即为扩展类名
String name = clazz.getSimpleName();
if (name.endsWith(type.getSimpleName())) {
name = name.substring(0, name.length() - type.getSimpleName().length());
}
return name.toLowerCase();
}
private void cacheName(Class<?> clazz, String name) {
// 只缓存第一个扩展类名
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, name);
}
}
private void saveInExtensionClass(Map<String, Class<?>> extensionClasses,
Class<?> clazz,
String name,
boolean overridden) {
// 扩展类名 => 扩展类
Class<?> c = extensionClasses.get(name);
// 没有解析或者可以覆盖
if (c == null || overridden) {
extensionClasses.put(name, clazz);
}
// 扩展名冲突, 抛出异常
else if (c != clazz) {
unacceptableExceptions.add(name);
String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
logger.error(duplicateMsg);
throw new IllegalStateException(duplicateMsg);
}
}
}
4.3.2 解析自适应扩展类
Dubbo SPI提供@Adaptive
注解注释自适应扩展类,其中,META-INF/dubbo/internal/
扩展类不支持覆盖配置。
java
public class ExtensionLoader<T> {
// 自适应扩展类
private volatile Class<?> cachedAdaptiveClass;
private void cacheAdaptiveClass(Class<?> clazz, boolean overridden) {
if (cachedAdaptiveClass == null || overridden) {
cachedAdaptiveClass = clazz;
}
// 仅支持一次设置
else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getName() + ", " + clazz.getName());
}
}
}
4.3.3 解析包装器扩展类
java
public class ExtensionLoader<T> {
// 包装类扩展类
private Set<Class<?>> cachedWrapperClasses;
private void cacheWrapperClass(Class<?> clazz) {
if (cachedWrapperClasses == null) {
cachedWrapperClasses = new ConcurrentHashSet<>();
}
cachedWrapperClasses.add(clazz);
}
}
4.3.4 解析激活扩展类
Dubbo SPI提供<font style="color:#74B602;">@Activate</font>
注解用于标注激活扩展类,通过cachedActivates
属性缓存注解值。
typescript
public class ExtensionLoader<T> {
// 激活扩展点: 扩展类标注@Activate, 如果存在多个扩展名只会缓存第一个扩展名作为激活点
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
private void cacheActivateClass(Class<?> clazz, String name) {
// 获取扩展类@Activate注解
Activate activate = clazz.getAnnotation(Activate.class);
// 缓存激活注解
if (activate != null) {
cachedActivates.put(name, activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(name, oldActivate);
}
}
}
}
4.4 创建实例
Dubbo提供ExtensionLoader#createExtension
创建扩展实例,通过ExtensionLoader#injectExtension
进行实例增强(setter),然后进行包装器类增强处理,最后进行扩展类实例初始化。
java
public class ExtensionLoader<T> {
// 缓存扩展点实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 缓存扩展名原始实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
// 异常扩展名
private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
// 创建扩展实例
private T createExtension(String name, boolean wrap) {
// 获取扩展类
Class<?> clazz = getExtensionClasses().get(name);
// 没有找到直接抛出异常
if (clazz == null || unacceptableExceptions.contains(name)) {
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 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);
}
}
}
4.4.1 扩展实例依赖注入
java
public class ExtensionLoader<T> {
// 扩展工厂
private final ExtensionFactory objectFactory;
// 扩展实例依赖注入
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
// 循环所有方法
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
// 方法标注@DisableInject注解, 不进行依赖注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获取依赖注入类型参数
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
// 获取方法名
String property = getSetterProperty(method);
// 获取方法@Inject注解
Inject inject = method.getAnnotation(Inject.class);
if (inject == null) {
injectValue(instance, method, pt, property);
} else {
// 禁止依赖注入
if (!inject.enable()) {
continue;
}
if (inject.type() == Inject.InjectType.ByType) {
injectValue(instance, method, pt, null);
} else {
injectValue(instance, method, pt, property);
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
// 获取方法名
private String getSetterProperty(Method method) {
return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
}
private void injectValue(T instance, Method method, Class<?> pt, String property) {
try {
// 获取依赖注入值
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);
}
}
}
4.4.2 初始化扩展实例
java
public class ExtensionLoader<T> {
// 扩展工厂
private final ExtensionFactory objectFactory;
// 初始化扩展实例
private void initExtension(T instance) {
// 如果扩展实现Lifecycle, 调用initialize进行初始化
if (instance instanceof Lifecycle) {
Lifecycle lifecycle = (Lifecycle) instance;
lifecycle.initialize();
}
}
}
4.5 获取扩展点
4.5.1 获取指定名称扩展点
Dubbo SPI重载getExtension
方法对外获取指定名称扩展类,其中,底层使用Holder持有当前扩展点实例,不过可以直接设计缓存实例对象。
java
public class ExtensionLoader<T> {
// 缓存扩展点实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 缓存扩展名原始实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
// 异常扩展名
private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
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();
}
// 获取或者创建持有Holder
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;
}
private Holder<Object> getOrCreateHolder(String name) {
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<>());
holder = cachedInstances.get(name);
}
return holder;
}
}
4.5.2 获取默认扩展点
java
public class ExtensionLoader<T> {
// 扩展接口默认扩展名, 通过@SPI(value=?)指定, 仅支持1个默认扩展名
private String cachedDefaultName;
public T getDefaultExtension() {
// 加载扩展类
getExtensionClasses();
// 缓存默认扩展名不能为空或者true
if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
return null;
}
// 指定名称获取扩展: @SPI(value=?)注解值就是默认扩展名
return getExtension(cachedDefaultName);
}
}
4.5.3 获取自适应扩展点
java
public class ExtensionLoader<T> {
// 自适应实例缓存: 每个扩展接口仅支持1个自适应扩展点
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
// 自适应扩展创建异常
private volatile Throwable createAdaptiveInstanceError;
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;
}
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.4 获取激活扩展点
Dubbo底层提供4个getActivateExtension
重载方法,方便用户根据URL获取激活扩展点。获取激活扩展点实例,需要匹配激活类匹配,以及常规扩展点。
匹配类型 | 匹配规则 |
---|---|
激活扩展点 | 激活扩展点解析逻辑,就是循环激活扩展点与URL参数进行匹配,如果匹配就添加到激活扩展集合。 激活扩展点匹配逻辑,包括是否允许激活扩展点匹配和激活扩展匹配。 1. 是否进行激活扩展点匹配:获取扩展名不能包含"-default" 2. 激活扩展点匹配 (1)激活扩展点注解匹配:@Activate(group={}, value={}) (2)激活扩展名匹配:参数扩展名 not_exsited (激活扩展名, -激活扩展名) |
常规扩展点 | 常规扩展点解析逻辑:循环传入参数扩展名进行匹配,如果解析到扩展名=default 就添加到已解析扩展到最前面,否则都追加到后面。 扩展名是否匹配:参数扩展名不是以去除符号(-)为前缀,以及扩展名不存在去除参数,如values={"timeout", "-timeout"} |
java
public class ExtensionLoader<T> {
// 激活扩展点: 扩展类标注@Activate, 如果存在多个扩展名只会缓存第一个扩展名作为激活点
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
public List<T> getActivateExtension(URL url, String key, String group) {
// 获取URL参数key=?对应值, 也就是扩展点名称
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
// @param values=扩展名称集合, group=扩展组
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
// 创建TreeMap: 也就是红黑树排序
TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
Set<String> loadedNames = new HashSet<>();
Set<String> names = CollectionUtils.ofSet(values);
// 激活扩展点匹配: 扩展点不包含"-default"值, 也就是去除(-)默认配置
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;
// apache注解
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;
}
// 1. paramGroup in (activateGroup)
// 2. activateName not_in (paramNames)
// 3. -activateName not_in (paramNames)
// 4. @Activate.value match url.params
// 5. 不重复加载
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)
&& !loadedNames.contains(name)) {
// 缓存扩展类与扩展实例映射
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
loadedNames.add(name);
}
}
if (!activateExtensionsMap.isEmpty()) {
activateExtensions.addAll(activateExtensionsMap.values());
}
}
List<T> loadedExtensions = new ArrayList<>();
// 扩展点匹配
for (String name : names) {
// -: 表示不匹配
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
// 控制不进行重复加载
if (!loadedNames.contains(name)) {
// DEFAULT_KEY = default
if (DEFAULT_KEY.equals(name)) {
// 将扩展点添加到激活扩展点之前
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
// 缓存扩展点实例
loadedExtensions.add(getExtension(name));
}
loadedNames.add(name);
} else {
// 重复加载, warn提示
String simpleName = getExtensionClass(name).getSimpleName();
logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name + ". Ignored Class Name: " + simpleName);
}
}
}
// 末尾添加到激活扩展点
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}
// 组匹配
private boolean isMatchGroup(String group, String[] groups) {
if (StringUtils.isEmpty(group)) {
return true;
}
// 找到相同组即可
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
// 激活点匹配
// 例如:@Active(value="key1:value1, key2:value2"), URL参数能模糊匹配参数即可
private boolean isActive(String[] keys, URL url) {
if (keys.length == 0) {
return true;
}
for (String key : keys) {
// @Active(value="key1:value1, key2:value2")
String keyValue = null;
if (key.contains(":")) {
String[] arr = key.split(":");
key = arr[0];
keyValue = arr[1];
}
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ((keyValue != null && keyValue.equals(v))
|| (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
return true;
}
}
}
return false;
}
}
4.6 手动添加扩展点
java
public class ExtensionLoader<T> {
// 缓存已加载扩展类, 格式: n1,n2,...=扩展类全限定名
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
// 扩展点对应扩展名称, 如果配置存在多个名称只会第一个解析值
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
public void addExtension(String name, Class<?> clazz) {
// 先执行扫描添加扩展
getExtensionClasses();
// 扩展类必须经常扩展接口
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Input type " + clazz + " doesn't implement the Extension " + type);
}
// 扩展类不能是接口
if (clazz.isInterface()) {
throw new IllegalStateException("Input type " + clazz + " can't be interface!");
}
// 非自适应扩展
if (!clazz.isAnnotationPresent(Adaptive.class)) {
if (StringUtils.isBlank(name)) {
throw new IllegalStateException("Extension name is blank (Extension " + type + ")!");
}
if (cachedClasses.get().containsKey(name)) {
throw new IllegalStateException("Extension name " + name + " already exists (Extension " + type + ")!");
}
// 缓存扩展类和扩展名映射关系
cachedNames.put(clazz, name);
cachedClasses.get().put(name, clazz);
} else {
// 不能重复添加自适应扩展
if (cachedAdaptiveClass != null) {
throw new IllegalStateException("Adaptive Extension already exists (Extension " + type + ")!");
}
cachedAdaptiveClass = clazz;
}
}
}
4.7 其他
4.7.1 是否包含扩展
java
public class ExtensionLoader<T> {
public boolean hasExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 获取扩展类
Class<?> c = this.getExtensionClass(name);
return c != null;
}
}
4.7.2 获取扩展类和名称
java
public class ExtensionLoader<T> {
// 扩展点对应扩展名称, 如果配置存在多个名称只会第一个解析值
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
// 获取扩展实例对应扩展点名称
public String getExtensionName(T extensionInstance) {
return getExtensionName(extensionInstance.getClass());
}
// 获取扩展类对应扩展点名称
public String getExtensionName(Class<?> extensionClass) {
getExtensionClasses();// load class
return cachedNames.get(extensionClass);
}
// 获取默认扩展名
public String getDefaultExtensionName() {
getExtensionClasses();
return cachedDefaultName;
}
// 获取扩展类
private Class<?> getExtensionClass(String name) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (name == null) {
throw new IllegalArgumentException("Extension name == null");
}
return getExtensionClasses().get(name);
}
}
4.7.3 获取已加载扩展点
java
public class ExtensionLoader<T> {
// 缓存扩展点实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 获取已加载扩展实例
public T getLoadedExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
Holder<Object> holder = getOrCreateHolder(name);
return (T) holder.get();
}
private Holder<Object> getOrCreateHolder(String name) {
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<>());
holder = cachedInstances.get(name);
}
return holder;
}
public Set<String> getLoadedExtensions() {
return Collections.unmodifiableSet(new TreeSet<>(cachedInstances.keySet()));
}
public List<T> getLoadedExtensionInstances() {
List<T> instances = new ArrayList<>();
cachedInstances.values().forEach(holder -> instances.add((T) holder.get()));
return instances;
}
}
4.7.4 获取扩展点原始实例
java
public class ExtensionLoader<T> {
// 缓存扩展名原始实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
public T getOriginalInstance(String name) {
getExtension(name);
Class<?> clazz = getExtensionClasses().get(name);
return (T) EXTENSION_INSTANCES.get(clazz);
}
}
4.7.5 获取支持扩展
java
public class ExtensionLoader<T> {
// 获取支持扩展名
public Set<String> getSupportedExtensions() {
// 获取扩展类
Map<String, Class<?>> clazzes = getExtensionClasses();
return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
}
// 获取支持扩展实例
public Set<T> getSupportedExtensionInstances() {
List<T> instances = new LinkedList<>();
Set<String> supportedExtensions = getSupportedExtensions();
if (CollectionUtils.isNotEmpty(supportedExtensions)) {
for (String name : supportedExtensions) {
instances.add(getExtension(name));
}
}
// 排序后返回
sort(instances, Prioritized.COMPARATOR);
return new LinkedHashSet<>(instances);
}
}
05 总结
SPI作为提供良好的扩展和可插拔实现,插件式微内核架构可以重要考虑的设计方案。Dubbo SPI机制比较灵活,提供默认、自适应、激活和普通扩展点支持,相比Java SPI更加灵活多变。