7-Dubbo的SPI扩展机制之自动激活扩展Activate源码解析
- group() 指定组条件。框架SPI定义了有效的组值。
- value() 指定URL条件中的参数键。
SPI提供程序可以调用ExtensionLoader。getActivateExtension(URL、String、String)方法以查找具有给定条件的所有已激活扩展。
7.2 获取自动激活扩展的源码
前面我们看了激活扩展是通过调用getActivateExtension方法来获取对象的,那接下来就来看下这个方法做了什么操作:
* @param url 服务的url
* @param key 用于获取扩展点名称的url参数键 比如监听器:exporter.listener,过滤器:params-filter,telnet处理器:telnet
*/
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑
/**
* 获取激活扩展.
*
* @param url 服务的url
* @param values 这个value是扩展点的名字 当指定了时候会使用指定的名字的扩展
* @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者使用这个过滤器还是提供者使用这个过滤器他们的group参数分表为consumer,provider
* @return 获取激活扩展.
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
//检查扩展加载器是否被销毁了
checkDestroyed();
// solve the bug of using @SPI's wrapper method to report a null pointer exception.
//创建个有序的Map集合,用来对扩展进行排序
Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(activateComparator);
//初始化扩展名字,指定了扩展名字values不为空
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
//扩展名字使用Set集合进行去重
Set<String> namesSet = new HashSet<>(names);
//参数常量是 -default 扩展名字是否不包含默认的
if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
//第一次进来肯定是没有缓存对象双重校验锁检查下
if (cachedActivateGroups.size() == 0) {
synchronized (cachedActivateGroups) {
// cache all extensions
if (cachedActivateGroups.size() == 0) {
//加载扩展类型对应的扩展类,这个具体细节参考源码或者《[Dubbo3.0.7源码解析系列]-5-Dubbo的SPI扩展机制与自适应扩展对象的创建与扩展文件的扫描源码解析》章节
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
//遍历所有的activates列表获取group()和value()值
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
} else {
continue;
}
//缓存分组值
cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
String[][] keyPairs = new String[activateValue.length][];
//遍历指定的激活扩展的扩展名字列表
for (int i = 0; i < activateValue.length; i++) {
if (activateValue[i].contains(":")) {
keyPairs[i] = new String[2];
String[] arr = activateValue[i].split(":");
keyPairs[i][0] = arr[0];
keyPairs[i][1] = arr[1];
} else {
keyPairs[i] = new String[1];
keyPairs[i][0] = activateValue[i];
}
}
//缓存指定扩展信息
cachedActivateValues.put(name, keyPairs);
}
}
}
}
// traverse all cached extensions
//遍历所有激活的扩展名字和扩展分组集合
cachedActivateGroups.forEach((name, activateGroup) -> {
//筛选当前扩展的扩展分组与激活扩展的扩展分组是否可以匹配
if (isMatchGroup(group, activateGroup)
//不能是指定的扩展名字
&& !namesSet.contains(name)
//也不能是带有 -指定扩展名字
&& !namesSet.contains(REMOVE_VALUE_PREFIX + name)
//如果在Active注解中配置了value则当指定的键出现在URL的参数中时,激活当前扩展名。
//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true)
&& isActive(cachedActivateValues.get(name), url)) {
//缓存激活的扩展类型映射的扩展名字
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
}
});
}
// `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them
ArrayList<T> extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size());
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
if (!DEFAULT_KEY.equals(name)) {
if (containsExtension(name)) {
extensionsResult.add(getExtension(name));
}
} else {
extensionsResult.addAll(activateExtensionsMap.values());
}
}
}
return extensionsResult;
} else {
// add extensions, will be sorted by its order
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
if (!DEFAULT_KEY.equals(name)) {
if (containsExtension(name)) {
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
}
}
}
}
return new ArrayList<>(activateExtensionsMap.values());
}
}
private void cacheActivateClass(Class<?> clazz, String name) {
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);
}
}
技术咨询与支持,可以扫描微信公众号进行回复咨询