Dubbo的自激活扩展说明
- Dubbo的激活扩展是指根据分组和url参数中的key,结合扩展类上的注解Activate,生成符合匹配条件的扩展实例,得到一个实例集合。
Dubbo中的应用场景
- 激活扩展在Dubbo中一个典型的应用场景就是过滤器(Filter), 在服务端收到请求之后,经过一系列过滤器去拦截请求,做一些处理工作,然后在真正去调用实现类。
@Activate注解说明
/**
* 激活。这个注解对于使用给定的条件自动激活特定的扩展非常有用,例如:当有多个实现时,@Activate可以用于加载特定的过滤器扩展。
* group()指定组条件。框架SPI定义了有效的组值。
* value()指定URL条件中的参数key。
* SPI提供程序可以调用ExtensionLoader.getActivateExtension(URL, String, String)来查找给定条件下所有激活的扩展。
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Activate the current extension when one of the groups matches. The group passed into
* {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
* 当其中一个组匹配时激活当前扩展。
* @return group names to match
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
String[] group() default {};
/**
* Activate the current extension when the specified keys appear in the URL's parameters.
* 当指定的键出现在URL的参数中时激活当前的扩展。
* 例如,给定@Activate(“cache, validation”),当前的扩展只会在URL的参数中出现cache或validation时返回。
*
* @return URL parameter keys
* @see ExtensionLoader#getActivateExtension(URL, String)
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
String[] value() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
* 关联顺序,是可选的,2.7.0以后被废弃
* @return extension list which should be put before the current one
*/
@Deprecated
String[] before() default {};
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
* 关联顺序,是可选的,2.7.0以后被废弃
* @return extension list which should be put after the current one
*/
@Deprecated
String[] after() default {};
/**
* Absolute ordering info, optional
* 绝对的顺序信息,可选的参数
* @return absolute ordering info
*/
int order() default 0;
}
源码分析(apache dubbo 2.7.8)
- ExtensionLoader关于getActivateExtension有4个重载方法,其中1,2,3最终都会调用方法4,所以我们直接分析方法4的逻辑
1. public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
2. public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
3. public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
/**
* Get activate extensions. 获取激活的扩展实例
*
* @param url url
* @param values extension point names 扩展点名称数组
* @param group group 分组
* @return extension list which are activated 返回被激活的扩展类集合
* @see org.apache.dubbo.common.extension.Activate
*/
4. public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
// 等同于 !names.contains("-default")
// 一般情况下,传入-default代表忽略@Activate注解上的属性,以传入的values来判断是否生成实例,'-xxx',表示忽略扩展名为xxx的扩展类
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
// 获取所有的扩展Class
getExtensionClasses();
// cachedActivates缓存的是name到Activate的映射
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
// 扩展类的名称,即配置文件中配置的名称
String name = entry.getKey();
// 注解Activate的信息
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
// 获取activate注解上group的值
activateGroup = ((Activate) activate).group();
// 获取activate注解上value的值
activateValue = ((Activate) activate).value();
// 兼容历史alibaba包的注解
} 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. isMatchGroup(group, activateGroup) 匹配分组, 注解中的groups数组是否包含参数中的group
// 2. !names.contains(name) names不能包含扩展类名称
// 3. !names.contains(REMOVE_VALUE_PREFIX + name) names不能包含'-扩展类名称'
// 4. isActive(activateValue, url)) 这里则根据配置的value值,和url中参数,是否匹配
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
// 生成扩展实例,加入到集合中
activateExtensions.add(getExtension(name));
}
}
// 排序(一般情况下,按照order排序)
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
// 定义一个加载扩展的集合
List<T> loadedExtensions = new ArrayList<>();
// 遍历其中的每一个name
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
// 如果name是没有以 '-' 作为开始,并且集合中也不包含'-name'
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
// 如果name的值为default,
if (DEFAULT_KEY.equals(name)) { // default
// 已加载扩展的集合不为空
if (!loadedExtensions.isEmpty()) {
// 将已扩展的实例集合加入到activateExtensions集合中,并清空loadedExtensions集合
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
// 获取到扩展实例后,添加到loadedExtensions集合中
loadedExtensions.add(getExtension(name));
}
}
}
// 遍历完成后,如果loadedExtensions不为空,则添加到activateExtensions集合中,并返回
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;
}
/**
* 是否激活
*/
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];
}
// 检查url中的参数是否包含key或者以.key结尾,然后在检查keyValue与url参数中对应key的value是否相等
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;
}
总结
- Dubbo的自激活扩展还是比较容易理解的,是根据方法参数,结合扩展类上Activate注解,过滤得到一个匹配的扩展实现集合,有了这个集合,我们可以实现一些定制化功能,比如拦截请求,监听处理等。
本文深入探讨了Dubbo的自激活扩展机制,详细解释了如何根据分组和URL参数,结合@Activate注解选择合适的扩展实例。通过分析Apache Dubbo 2.7.8的源码,揭示了激活扩展在过滤器(Filter)应用中的作用,帮助理解其在服务拦截和定制功能实现中的重要性。
591

被折叠的 条评论
为什么被折叠?



