dubbo SPI之@Activate注解

本文基于dubbo 2.7.22分析:dubbo SPI之@Activate注解

@Activate

用于指明该SPI实现类在什么条件下生效,
下面看下@org.apache.dubbo.common.extension.Activate注解中各个属性的含义

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {String[] group() default {};String[] value() default {};@DeprecatedString[] before() default {};@DeprecatedString[] after() default {};int order() default 0;
}
  1. order 用于多个SPI实现类之间排序, beforeafter 也用是用于排序但是被标记为过期,不建议使用. 只用order属性,这样逻辑也简单一点.

  2. group 数组类型,用于指明该Filter作用于provider或是consumer端,可以指明该SPI实现类providerconsumer端同时生效. 但建议不要将provider端和consumer端的逻辑混到同一个SPI实现类

  3. value 数组类型,用于指明该SPI实现类生效的条件, 可以指定多个条件, 只要有一个满足该SPI实现类就生效;
    如果不指定value,则默认生效。value的格式有如下几种, (以Dubbo Filter SPI实现类为例):

    @Activate(group = {CONSUMER}) 
    value属性不填写, 默认生效
    
    @Activate(group = {CONSUMER}, value = "filter_enable:true") 
    当url参数中存在参数filter_enable且参数值是true时该Filter生效, 
    如果写了冒号,那后面一定要有值,否则报错
    
    @Activate(group = {PROVIDER}, value = "filter_enable") 
    当url参数中存在参数filter_enable且参数值任意不为null时该Filter生效
    
    @Activate(group = {PROVIDER}, value = {"param1:yes","param2:ok"}) 
    多个参数支持, 当url参数中存在参数param1且参数值为yes, 
    或者url参数中存在参数param2且参数值为ok时该Filter生效
    

@Activate如果不写value属性值, SPI实现默认是生效的, 如何默认不生效??

  1. 除了通过value属性指定生效条件外,(推荐使用该方式)
  2. 不使用@Activate注解,则默认不会生效,当期望SPI实现生效时,可以通过如下配置指定
dubbo.provider.filter=providerFilter2,-providerFilter1  #设置provider段Filter
dubbo.consumer.filter=-monitor  #禁用consumer端的monitor Filter

示例为: 启用名称为providerFilter2的Filter实现, -表示禁用, -providerFilter1表示禁用名称为providerFilter1的Filter实现

dubbo.provider.filter=providerFilter2,-default 

示例为: 关闭默认启用的provider端Filter, 只开启了providerFilter2

dubbo.provider.filter=providerFilter2,providerFilter1,default 

示例为: providerFilter2,providerFilter1两个Filter放在default Filter之前

注意:

  1. 这些SPI实现类必须要配置到dubbo的SPI配置文件里, 否则无法被加载
  2. default 代表的是多个默认启用的SPI实现,并不是具体的一个实现类

踩坑点:

如果@Activate注解value属性写的filter.test, 但配置的参数是my.filter.test, 多了一个my,该SPI实现类也会生效. 完整示例如下:

dubbo.provider.parameters.my.filter.test=aa
或者
dubbo.application.parameters.my.filter.test=aa

原因是源码内部还会通过java.lang.String#endsWith方法去匹配. 具体见:ExtensionLoader#isActive源码:
在这里插入图片描述

源码分析

ExtensionLoader#getActivateExtension(URL url, String[] values, String group)源码分析:
该方法用于根据Url中的参数,获取(激活的)符合条件SPI实现类.

参数values的值部分可由用户设置,最常用的可能是对于dubbo filter的加载控制,但不仅仅局限于此, 还可以对dubbo.consumer.listener, dubbo.provider.listener的控制, 有些则是dubbo内部源码传固定值

public List<T> getActivateExtension(URL url, String[] values, String group) {List<T> activateExtensions = new ArrayList<>();// TreeMap传入比较器,用于多个实现类之间自动根据Activate#order的值排序TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);Set<String> loadedNames = new HashSet<>();Set<String> names = CollectionUtils.ofSet(values);// 如果参数传入-default则表示关闭默认启用的SPI实现,其实就是对Filter处理// 例如: dubbo.provider.filter=providerFilter2,-default // 则表示关闭默认启用的provider端Filter, 只开启了providerFilter2if (!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;}// 匹配group, 就是获取作用在provider端还是consumer端的实现// 如果values已经指定了加载某个名称的SPI实现,则此时不加载, 等默认的加载完成在加载// -开的表示禁用某个SPI实现// isActive方法根据@Activate注解的value值和url中的参数判断,是否符合条件if (isMatchGroup(group, activateGroup)&& !names.contains(name)&& !names.contains(REMOVE_VALUE_PREFIX + name)&& isActive(activateValue, url)&& !loadedNames.contains(name)) {activateExtensionsMap.put(getExtensionClass(name), getExtension(name));loadedNames.add(name);}}if (!activateExtensionsMap.isEmpty()) {activateExtensions.addAll(activateExtensionsMap.values());}}List<T> loadedExtensions = new ArrayList<>();for (String name : names) {// 加载names中指定的SPI实现, 如果name是default字符串, 说明循环之前加载的SPI实现都排在默认的之前//例如 filter1, filter2, default, filter3//default代表的是多个默认的启用的SPI实现,并不是具体的SPI实现if (!name.startsWith(REMOVE_VALUE_PREFIX)&& !names.contains(REMOVE_VALUE_PREFIX + name)) {if (!loadedNames.contains(name)) {if (DEFAULT_KEY.equals(name)) {if (!loadedExtensions.isEmpty()) {activateExtensions.addAll(0, loadedExtensions);loadedExtensions.clear();}} else {loadedExtensions.add(getExtension(name));}loadedNames.add(name);} else {// If getExtension(name) exists, getExtensionClass(name) must exist, so there is no null pointer processing here.String simpleName = getExtensionClass(name).getSimpleName();logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name +". Ignored Class Name: " + simpleName);}}}if (!loadedExtensions.isEmpty()) {activateExtensions.addAll(loadedExtensions);}return activateExtensions;}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部