Dubbo-SPI
1、扩展点机制
什么是SPI
- SPI(Service Provider Interface)实际是一种服务提供者的发现机制,通过规范约束加载服务实现类实现接口与实现的扩展与解耦。
Java SPI
- Java SPI是Java内置的一套SPI机制,通过ServiceLoader加载META-INF/services目录下创建接口的全限定名文件,文件内容为接口实现类的全限定名,以换行符分隔
- Java SPI的缺点是会一次性加载并实例化所有的扩展点实现,这无疑会造成资源浪费的情况。
Dubbo SPI
- Dubbo SPI对比Java SPI做了改进和优化,添加了IOC和AOP的支持,优化的异常信息,扩展类使用时初始化,
- Dubbo SPI加载META-INF/dub目录下的配置文件,文件名为接口全限定名,文件内容为key-value键值对,value为接口实现类的全限定名,key为获取value的关键字。接口必须添加@SPI注解,使用ExtensionLoader加载扩展类。
2、扩展点特性
自动包装
- ExtensionLoader 在加载扩展时,如果发现这个扩展类包含其他扩展点作为构造函数的参数 , 则这个扩展类就会被认为是 Wrapper类(包装类)
- 包装类与构造器参数实现了共同的扩展接口,扩展点添加增强功能。
- 例如ProtocolFilterWrapper包装类,
自动装配
- Dubbo通过setter方法判断成员是否为其他扩展类,如果是则通过ExtensionLoader注入扩展类。
- 一个接口会有多个实现类,那么自动装配的时候如何判断应该注入哪个实现类?答案是自适应
自适应
- 扩展点方法需要URL参数,Dubbo提供了@Adaptive注解,通过@Adaptive注解和URL参数判断最终选取的扩展实现类。
- @Adaptive可以用在类和方法上
标记点 | 作用 |
---|---|
方法 | 通过参数动态获得实现类,Dubbo会默认生成一个Adaptive的代理类,在代理类中通过URL参数获取具体实现类,然后调用目标方法。 |
类 | 当@Adaptive作用在实现类上时,Dubbo就不会再自动生成Adaptive的代理类,而是使用@Adaptive标记的实现类作为,可以说是从动态代理改为了静态代理,所以只能有一个实现类使用@Adaptive注解。 |
自动激活
- Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker等扩展点,可以有多个实现类,使用@Activate注解可以针对不同场景使用不同的实现类。
- @Activate可以标记在类、和方法上。主要使用在有多个扩展点实现、需要根据不同条件被激活的场景中。
注解
@SPI
- @SPI注解用在接口上,主要作用是标记这个接口是Dubbo的SPI接口
- @SPI有一个默认值value,可以指定接口的默认实现。
@Adaptive
修饰接口方法,实现类
- 方法:Dubbo默认会生成一个动态代理类,参数包含URL对象,可以根据URL参数动态获取实现类。
- 类:当再实现类使用此注解时,Dubbo不会生成动态代理类,而是使用此类作为静态代理。扩展点可以有多个实现类,但至多只能有一个类使用@Adaptive注解。
@Activate
- 一般用在接口上,为扩展点赋予条件属性,如分组、排序、url的key等信息,可以在条件的不同在不同的场景中激活扩展功能。
3、ExtensionLoader
T getExtension(String name)
主要分为2步
- 根据name获取class
- 根据class获取实例信息
这2步都使用了Map缓存。先从缓存中获取信息,没有则加载然后初始化并放入缓存。
/** 方法大致分为两步,1、获取class的信息,2、获取class的实例信息*/
public T getExtension(String name) {
。。。 // 省略代码
// 1、获取或创建class的持有者
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 2、创建class的实例
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
/** 从缓存cachedInstances中获取,如果没有则创建一个空的holder*/
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;
}
/**
1、getExtensionClasses会在cachedClasses缓存中获取class信息
2、如果缓存中不存在,则去META-INF/dubbo、META-INF/dubbo/internal、META-INF/services这三个目录下扫描文件
并将文件信息缓存至cachedClasses和cachedNames,然后根据参数name得到class信息
3、完成了配置文件加载后,在EXTENSION_INSTANCES中查询根据class查询是否存在class的实例缓存,如果没有则根据class创建实例并插入到EXTENSION_INSTANCES缓存中
上面3步完成了class的实例获取,接着需要对实例中的成员信息进行注入
4、注入的条件是必须是setter方法,且参数只能有一个
5、然后去SpiExtensionFactory和SpringExtensionFactory查找符合class与name的实例,如果存在该实例,则通过反射注入。
在2步,加载配置文件的类信息时,如果类的构造方法的参数是当前类的类型,则会将类信息缓存至cachedWrapperClasses
6、类初始化以后,需要自动装配,循环cachedWrapperClasses,根据上面的实例信息初始化其他类实例。再对实例信息进行成员变量注入
7、对包装类进行初始化并注入成员变量
*/
private T createExtension(String name) {
// 根据名称获取class对象
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 (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
T getAdaptiveExtension()
获取适配扩展类,@Adaptive修饰接口类无效,修饰实现类方法无效,可以修饰接口的方法和实现类。
/** 获取扩展点适配器类,如果缓存不存在,则加载配置文件并初始化类信息*/
public T getAdaptiveExtension() {
// 缓存中获取实例
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
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);
}
}
}
} else {
throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
List getActivateExtension(URL url, String[] values, String group)
/** 方法包含两个功能,1、先获取加了@Activate注解的实现类信息,然后初始化实例并返回,2、根据url参数解析出name,然后根据name获取实例信息*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
// 获取加了@Activate注解的实现类信息,然后初始化实例
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;
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;
}
if (isMatchGroup(group, activateGroup)) {
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
exts.add(ext);
}
}
}
exts.sort(ActivateComparator.COMPARATOR);
}
// 根据url参数解析出name,然后根据name获取实例信息
List<T> usrs = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}