Dubbo-SPI系列文章目录

Dubbo-SPI(一)-加载机制概述
Dubbo-SPI(二)-@SPI注解
Dubbo-SPI(三)-getExtension实现原理
Dubbo-SPI(四)-@Adaptive注解
Dubbo-SPI(五)-@Activate注解


文章目录

  • Dubbo-SPI系列文章目录
  • @Activate注解
  • getActivateExtension实现原理
    • 1.isMatchGroup
    • 2.isActive
    • 3.ActivateComparator
  • 总结

@Activate注解

/*** Activate. This annotation is useful for automatically activate certain extensions with the given criteria,* 激活,这个注解 用于 自动激活 特定的扩展 当满足给定条件的时候,比如在Filter应用场景中* for examples: <code>@Activate</code> can be used to load certain <code>Filter</code> extension when there are* multiple implementations.* <ol>* <li>{@link Activate#group()} specifies group criteria. Framework SPI defines the valid group values.* <li>{@link Activate#value()} specifies parameter key in {@link URL} criteria.* </ol>* group():特定的分组条件* value():特定的参数key 在URL中* SPI provider can call {@link ExtensionLoader#getActivateExtension(URL, String, String)} to find out all activated* extensions with the given criteria.* SPI 提供 可以通过调用getActivateExtension方法 找出 所有 被激活的 扩展 根据 给定的 条件*  * @see SPI* @see URL* @see ExtensionLoader*/
@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.* 激活 当前扩展 当 有一个group匹配的时候,group参数 传入getActivateExtension方法中* 可以设置多个group* @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.* 激活 当前扩展 当 指定的参数key 出现在URL的参数中* <p>* For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when* there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.* </p>** @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** @return extension list which should be put before the current one*  返回 哪些扩展列表 将被返回 在 当前扩展被激活之前*/@DeprecatedString[] before() default {};/*** Relative ordering info, optional* Deprecated since 2.7.0** @return extension list which should be put after the current one* 返回 哪些扩展列表 将被返回 在 当前扩展被激活之后*/@DeprecatedString[] after() default {};/*** Absolute ordering info, optional** @return absolute ordering info*/// 优先级int order() default 0;
}

getActivateExtension实现原理

getActivateExtension方法可以获取 所有 自动激活扩展点,参数分别是URL、指定的key、指定的group
getActivateExtension 对 getExtension 的依赖比较重

主流程分为四步骤:

  1. 检查缓存,如果缓存中没有,则 初始化 所有 扩展类实现的集合
  2. 遍历整个@Activate注解集合,根据URL传入的匹配条件,得到 所有 符合 激活条件的 扩展类实现,然后 根据@Activate中的before after order进行排序
  3. 遍历 所有 用户自定义扩展类名称,根据用户URL配置的顺序,调整 扩展点激活顺序
  4. 返回所有自动激活类集合
  5. 如果URL中参数传入了-default,则所有的 默认@Activate 都不会被激活;如果传入了“-X”,则X扩展点也不会被激活。
/**** @param url url* @param key 用于 获取扩展点名称 的url参数key* @param group 指定分组* @return 自动激活扩展集合*/
public List<T> getActivateExtension(URL url, String key, String group) {// 根据URL的key获取到对应的value,使用,对value做分割String value = url.getParameter(key);return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}/*** Get activate extensions.67** @param url    url* @param values extension point names 指定自动激活扩展名数组* @param group  group 指定的group* @return extension list which are activated* @see org.apache.dubbo.common.extension.Activate*/
public List<T> getActivateExtension(URL url, String[] values, String group) {// 自动激活扩展集合List<T> activateExtensions = new ArrayList<>();// solve the bug of using @SPI's wrapper method to report a null pointer exception.// 修复 使用SPI包装方法 导致空指针的bug// key是Class类对象,value是实例TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);// 下面的names就是指定的valuesList<String> names = values == null ? new ArrayList<>(0) : asList(values);// 如果 names不包含"-default",则遍历 缓存中所有的 默认!!!自动激活扩展// ex: <dubbo:service filter="-default" /> ,代表移除所有默认过滤器if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {// 这里面的cacheActivateClass方法会缓存 扩展名 + 自动激活类对象getExtensionClasses();// 循环 扩展名 + 自动激活类对象 缓存for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {// 扩展名String name = entry.getKey();// 自动激活类对象Object activate = entry.getValue();String[] activateGroup, activateValue;// 得到@Activate注解的group和value值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;}// 如果 注解中的groups数组包含指定的group// 且 指定扩展名数组names不包含该自动激活扩展类名称// 且 指定扩展名数组names也不包含'-该自动激活扩展类名称'// 且 根据注解中的value值(activateValue),和url中参数比较是否匹配// 综上:该默认扩展点name不在要获取的扩展点集合names中(下面会专门处理要获取的扩展点) && 要获取的扩展点集合names 不存在 该默认扩展点name的自反 && 此扩展点的value与url匹配if (isMatchGroup(group, activateGroup)&& !names.contains(name)&& !names.contains(REMOVE_VALUE_PREFIX + name)&& isActive(activateValue, url)) {// 获取 指定名称name的拓展类,放入activateExtensionsMap集合activateExtensionsMap.put(getExtensionClass(name), getExtension(name));}}if(!activateExtensionsMap.isEmpty()){// 保存到activateExtensions列表    // 到此为止,目前activateExtensions中都是默认的自动激活扩展activateExtensions.addAll(activateExtensionsMap.values());}}// 加载扩展集合List<T> loadedExtensions = new ArrayList<>();// 针对指定的扩展名进行专门的处理for (int i = 0; i < names.size(); i++) {String name = names.get(i);// 如果name是没有以 '-' 作为开始,并且集合中也不包含'-name'// 如:既含有name,又含有-name,这样自相矛盾的不处理)if (!name.startsWith(REMOVE_VALUE_PREFIX)&& !names.contains(REMOVE_VALUE_PREFIX + name)) {if (DEFAULT_KEY.equals(name)) {// 如果name=default 且 已加载扩展实例集合不为空if (!loadedExtensions.isEmpty()) {// 将已加载的扩展实例集合加入到activateExtensions集合中,并清空loadedExtensions集合// 如果当前指定扩展点name=default,则将 loadedExtensions 转移到 activateExtensions 首位// 用于排序,这样default就排到后面了// 例如,<dubbo:service filter="demo,default,demo2" /> ,则 DemoFilter 就会放在默认的过滤器前面。activateExtensions.addAll(0, loadedExtensions);loadedExtensions.clear();}} else {// name!=default,根据name获取扩展类实例,放到loadedExtensions列表中loadedExtensions.add(getExtension(name));}}}// 如果 循环完指定的扩展名之后 loadedExtensions仍不为空// 就是上面else里面的逻辑// 说明loadedExtensions里面的扩展 都是 配置在default之后的// 添加到default排序之后if (!loadedExtensions.isEmpty()) {activateExtensions.addAll(loadedExtensions);}return activateExtensions;
}

1.isMatchGroup

/**** @param group 指定的group* @param groups @Activate注解中的group* @return boolean*/
private boolean isMatchGroup(String group, String[] groups) {// 如果指定的group为空,直接返回匹配成功if (StringUtils.isEmpty(group)) {return true;}// 否则循环@Activate注解中的group,判断是否包含指定的groupif (groups != null && groups.length > 0) {for (String g : groups) {if (group.equals(g)) {return true;}}}return false;
}

2.isActive

/**** @param keys @Activate注解中的value* @param url URL* @return boolean*/
private boolean isActive(String[] keys, URL url) {// 如果@Activate的value为null,则返回激活if (keys.length == 0) {return true;}// 循环@Activate的valuefor (String key : keys) {// @Active(value="key1:value1, key2:value2")// 针对@Activate的value中有:的解析String keyValue = null;if (key.contains(":")) {String[] arr = key.split(":");key = arr[0];keyValue = arr[1];}// 循环URL的参数,进行匹配for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {String k = entry.getKey();String v = entry.getValue();// URL中的key=@Activate的value的=key 或 URL中的key 以 @Activate的value的=key 结尾// 且 (keyValue不为空 + URL中的value=@Activate的value的keyValue 或 keyValue为空 + URL中的value不为空)// 满足以上条件,认为是激活状态if ((k.equals(key) || k.endsWith("." + key))&& ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {return true;}}}return false;
}

3.ActivateComparator

ActivateComparator首先通过parseActivate解析注解信息到ActivateInfo
然后对于有配置before或after的 则根据其值 进行升序排序
否则则通过order(没有指定默认为0)来排序,大于返回1,否则返回-1

@Override
public int compare(Object o1, Object o2) {// .....// 分别获取o1 o2的Activate类对象ActivateInfo a1 = parseActivate(o1.getClass());ActivateInfo a2 = parseActivate(o2.getClass());// before 或 after有值 且 o1有SPI注解if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) {ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(inf);if (a1.applicableToCompare()) {// 获取o2的扩展名String n2 = extensionLoader.getExtensionName(o2);// o1.before中包含o2.name,则o1<o2if (a1.isLess(n2)) {return -1;}// o1.after中包含o2.name,则o1>o2if (a1.isMore(n2)) {return 1;}}if (a2.applicableToCompare()) {// 获取o1的扩展名String n1 = extensionLoader.getExtensionName(o1);// o2.before中包含o1.name,则o1>o2if (a2.isLess(n1)) {return 1;}// o2.after中包含o1.name,则o1<o2if (a2.isMore(n1)) {return -1;}}}// never return 0 even if n1 equals n2, otherwise, o1 and o2 will override each other in collection like HashSetreturn a1.order > a2.order ? 1 : -1;
}// 将Activate类对象信息放到ActivateInfo中
private ActivateInfo parseActivate(Class<?> clazz) {ActivateInfo info = new ActivateInfo();if (clazz.isAnnotationPresent(Activate.class)) {Activate activate = clazz.getAnnotation(Activate.class);info.before = activate.before();info.after = activate.after();info.order = activate.order();} else {com.alibaba.dubbo.common.extension.Activate activate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);info.before = activate.before();info.after = activate.after();info.order = activate.order();}return info;
}// 内部静态类
private static class ActivateInfo {// 前置扩展名数组private String[] before;// 后置扩展名数组private String[] after;private int order;private boolean applicableToCompare() {return ArrayUtils.isNotEmpty(before) || ArrayUtils.isNotEmpty(after);}private boolean isLess(String name) {return Arrays.asList(before).contains(name);}private boolean isMore(String name) {return Arrays.asList(after).contains(name);}
}

总结

  • 系统默认的自动激活扩展

    • 筛选:如果参数中没有-default选项,则加载默认的@Activate扩展。这里得到的 所有默认@Activate扩展点,均需要 根据传入参数的group、url配置的key等 进行匹配筛选
    • 排序:默认@Activate扩展点的排序,是按照@Activate配置的before、after、order等参数进行排序
  • URL指定的自动激活扩展

    • 筛选:由于扩展点名称是作为参数传递进来的,因此需要进行 扩展是否加载的逻辑判断
    • 排序:对于URL参数传入的扩展点,用户可以通过 修改URL中参数中 扩展点顺序 来调整扩展点的激活顺序

Dubbo-SPI(五)-@Activate注解相关推荐

  1. dubbo扩展点-Activate注解

    1.Activate的作用 Activate:可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件. 以上定义有2个关键词,自动激活和加载条件 可以参考此篇文章: 解释: ...

  2. Dubbo 2.7.3源码分析——Dubbo SPI(二)

    老板,救救孩子?关注一下吧! 作者 肥又君 前情回顾 在前面介绍了Dubbo SPI 中 ExtensionLoader 的基础使用和原理,本篇重点介绍 Dubbo SPI 对 @Adaptive 注 ...

  3. dubbo SPI之@SPI、@Adaptive注解, 以及什么时候动态生成$Adaptive代码

    本文基于dubbo2.7.7对如下三个问题分析 @SPI注解的作用 @Adaptive注解的作用,放在Type和Method上的区别和注意点 什么时候动态生成和编译xxx$Adaptive代码 @SP ...

  4. JDK、Spring、Dubbo SPI 原理介绍

    导读: 需求变化是程序员生命中唯一不变的事情,本文将介绍 JDK/Spring/Dubbo 中的 SPI 机制,以此来帮助我们编写出一套可扩展性强,易于维护的代码框架. 文|杨亮 网易云商高级 Jav ...

  5. Dubbo源码分析系列-深入Dubbo SPI机制

    导语   在之前的博客中介绍过关于Java中SPI的机制,也简单的分析了关于Java中SPI怎么去使用.SPI的全称Service Provider Interface,是一种服务发现机制.SPI的本 ...

  6. Dubbo SPI的基础Cooma微容器

    为什么80%的码农都做不了架构师?>>>    从dubbo的SPI理念开始,由于dubbo的项目Leader一开始就把其定义成一个方便扩展的服务框架,所以在dubbo的架构设计中始 ...

  7. Activate注解

    Activate注解 被该注解修饰的接口,扩展类可能会被加载 ProtocolFilterWrapper.buildInvokerChain @Documented @Retention(Retent ...

  8. Dubbo SPI机制(上):一个普通的扩展类是如何加载的

    这一篇我们先不讲Dubbo中的具体业务逻辑,我们来打基础,聊一聊Dubbo中的SPI机制. Dubbo SPI是干啥的 了解一个技术,得先知道它是为了解决什么问题而产生的.那么Dubbo SPI是干什 ...

  9. 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解

    本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...

最新文章

  1. java培训教程:什么是匿名内部类?怎样创建匿名内部类?
  2. 谷歌称居家办公影响工作效率!2021 年将回归线下办公
  3. Github、Jekyll 搭建及优化静态博客方法指南
  4. hiho一下 第七周 Hihocoder #1043 : 完全背包
  5. Laravel的初始化安装 1
  6. 计算机网络( 二十二)-数据链路层(补充)
  7. Redis(十二):Redis事务的基本操作
  8. 【程序设计】变量的作用域
  9. 20145335 《信息安全系统设计基础》课程总结
  10. MYSQL 5.7.26 二进制版本安装
  11. mysql函数使用_mysql函数应用
  12. 深入理解java虚拟机 - jvm高级特性与最佳实践(第三版)_深入理解Java虚拟机,JVM高级特性与最佳实践!...
  13. 利用二叉链表创建二叉树_利用递归解LeetCode第111题:二叉树的最小深度
  14. python 干什么工作具有明显优势-为什么这么多人学Python?Python在就业上有什么优势?...
  15. 580集photoshop顶尖视频教程送给你,设计总监手把手带你学ps!
  16. android现状及发展趋势,2021年Android手机现状分析
  17. Matlab中传递函数的几种输入方式
  18. 《雨巷》-- 戴望舒
  19. Instant Client package is required for Baic and TNS connection
  20. 如何计算机器人的工作范围,工业机器人能做什么工作_工业机器人的工作范围...

热门文章

  1. 【转】最好最牛的101个网站 【达人必修】
  2. 如何解决Authentication failure问题
  3. CDGA|盘点近年全国各部门、各城市颁布数字化转型的政策
  4. Cocos2dx--纹理使用
  5. Fragment详解
  6. Python 播放音频与录音
  7. 【AHK】如何实现通达信电脑端 核按钮清仓
  8. EDIUS中的局部区域该怎么进行模糊
  9. 手机相册里android照片的来源,为什么手机相册总是出现不知道哪里来的照片?|实用...
  10. 计算机考试countif函数,16种常见的COUNTIF函数公式设置