7.1 Activate 扩展说明
此注解对于使用给定条件自动激活某些扩展非常有用,例如:@Activate 可用于在多个实现时加载某些筛选器扩展
- group:指定组条件,框架 SPI 定义了有效的组值 CommonConstants.PROVIDER / CommonConstants.CONSUMER
- value:指定 URL 条件中的参数键
SPI 提供程序可以调用 ExtensionLoader#getActivateExtension(URL url, String key, String group)
方法查找具有给定条件的所有已激活扩展,比如:DefaultFilterChainBuilder#buildInvokerChain 过滤器扩展对象的获取,通过调用 getActivateExtension 方法的代码
List<Filter> filters = ScopeModelUtil.getExtensionLoader(Filter.class, moduleModels.get(0)).getActivateExtension(url, key, group);
7.2 获取自动激活扩展
前面激活扩展是通过调用 getActivateExtension 方法来获取对象的,那么接下来就来看下该方法是如何操作的
/**
* This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)}
* @param url 服务 url
* @param key 用于获取扩展点名称 url 参数键
比如:监听器-exporter.listener、过滤器-params-filter、telnet 处理器-telnet
* @param group group 用于筛选的分组,比如过滤器中使用此参数来区分消费者还是提供者使用这个过滤器
group 参数分别为 consumer、provider
* @return 已激活的扩展列表.
*/
public List<T> getActivateExtension(URL url, String key, String group) {
// 从 URL 中获取指定的参数值
String value = url.getParameter(key);
// 获取激活的扩展点
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
上面的重载方法都是用来转换参数的,下面这个方法才是真正的逻辑
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)
: Arrays.stream(values).map(StringUtils::trim).collect(Collectors.toList());
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) {
// 加载扩展类型对应的扩展类
// 具体的方法执行流程可以看 《5-Dubbo Extension Adaptive 自适应扩展源码解析》章 5.2 小节内容
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();
activateValue = ((Activate) activate).value();
} else if (Dubbo2CompactUtils.isEnabled()
&& Dubbo2ActivateUtils.isActivateLoaded()
&& Dubbo2ActivateUtils.getActivateClass().isAssignableFrom(activate.getClass())) {
activateGroup = Dubbo2ActivateUtils.getGroup((Annotation) activate);
activateValue = Dubbo2ActivateUtils.getValue((Annotation) activate);
} 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));
}
});
}
if (namesSet.contains(DEFAULT_KEY)) {
// will affect order
// `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 (String name : names) {
if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
continue;
}
if (DEFAULT_KEY.equals(name)) {
extensionsResult.addAll(activateExtensionsMap.values());
continue;
}
if (containsExtension(name)) {
extensionsResult.add(getExtension(name));
}
}
return extensionsResult;
} else {
// add extensions, will be sorted by its order
for (String name : names) {
if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
continue;
}
if (DEFAULT_KEY.equals(name)) {
continue;
}
if (containsExtension(name)) {
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
}
}
return new ArrayList<>(activateExtensionsMap.values());
}
}
具体解释一下源码中是如何解析 @Active 注解 value、group 中,以如下配置为例:
@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, value = {"key1:value1", "key2:value2"})
cachedActivateGroups Map 集合会有一条元素,比如在 file 文件中配置的 key 为 demo,那么整个集合会是:
{demo=[provider, consumer]}
keyPairs 二维数组集合会有两个元素,如上举例整个集合会是:
[[key1, value1], [key2, value2]]
遍历的 cachedActivates 集合元素,其实是加载扩展类时已经设置进去了,回顾一下来自 5.2.3 小节的 ExtensionLoader#loadClass 方法,扫描扩展类型时与激活扩展的相关扫描代码,这里 loadClass 方法关于这块核心代码:
// 无自适应注解,也没有构造器是扩展类型参数, name 在扩展文件中找到的话那就是等号前面那个值
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)) {
// 前面判断了若不是 Adaptive 也不是 Wrapper 类型则我们可以来判断是否为 Activate 类型
// 如果是的话调用 cacheActivateClass 方法将扩展缓存进 cachedActivates 缓存中
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
// 将扩展类型加入结果集合 extensionClasses 中,不允许覆盖的话出现相同名字扩展将抛出异常
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
主要就是看 cacheActivateClass 方法的判断逻辑
private void cacheActivateClass(Class<?> clazz, String name) {
Activate activate = clazz.getAnnotation(Activate.class);
// 注解存在则加入激活注解缓存
if (activate != null) {
cachedActivates.put(name, activate);
} else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded()) {
// support com.alibaba.dubbo.common.extension.Activate
Annotation oldActivate = clazz.getAnnotation(Dubbo2ActivateUtils.getActivateClass());
if (oldActivate != null) {
cachedActivates.put(name, oldActivate);
}
}
}
主要在两种条件下该扩展类会被自动激活扩展
- 当前扩展类上存在 @Activate 注解修饰
- 为了适配 Dubbo 2.x 版本逻辑,若扩展类上使用 com.alibaba.dubbo.common.extension.Activate 注解修饰,也可以被自动激活
7.3 Activate 激活扩展类加载流程图
参考文献
https://www.ktyhub.com/zh/chapter_dubbo/7-extension-activate/