Dubbo SPI之Activate详解

前期准备

一. 增加pom

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>dubbo</artifactId>
   <version>2.5.3</version>
</dependency>

二. 添加代码

重要声明:本节演示的源码来自于网络的一片文章,看例子讲解的很透彻,很能说明问题就直接引用过来了,没法注明出处,如果有知道的请和我联系下,我添加上——尊重原创,从我做起

1. shuqi.dubbotest.spi.adaptive.AdaptiveExt2 作为需要被扩展的接口,注意要加上@SPI注解

package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.SPI;

@SPI
public interface ActivateExt1 {
    String echo(String msg);
}


2. 上面接口的五个实现类

a. shuqi.dubbotest.spi.activate.ActivateExt1Impl1
package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.Activate;

/**
 * @author linyang on 18/4/20.
 */
@Activate(group = {"default_group"})
public class ActivateExt1Impl1 implements ActivateExt1 {

    public String echo(String msg) {
        return msg;
    }
}

b. shuqi.dubbotest.spi.activate.GroupActivateExtImpl
package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.Activate;

/**
 * @author linyang on 18/4/20.
 */
@Activate(group = {"group1", "group2"})
public class GroupActivateExtImpl implements ActivateExt1 {
    public String echo(String msg) {
        return msg;
    }
}
c. shuqi.dubbotest.spi.activate.OrderActivateExtImpl1
package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.Activate;

/**
 * @author linyang on 18/4/20.
 */
@Activate(order = 2, group = {"order"})
public class OrderActivateExtImpl1 implements ActivateExt1 {
    public String echo(String msg) {
        return msg;
    }
}
d. shuqi.dubbotest.spi.activate.OrderActivateExtImpl2
package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.Activate;

/**
 * @author linyang on 18/4/20.
 */
@Activate(order = 1, group = {"order"})
public class OrderActivateExtImpl2 implements ActivateExt1 {
    public String echo(String msg) {
        return msg;
    }
}
e. shuqi.dubbotest.spi.activate.ValueActivateExtImpl
package shuqi.dubbotest.spi.activate;

import com.alibaba.dubbo.common.extension.Activate;

/**
 * @author linyang on 18/4/20.
 */
@Activate(value = {"value1"}, group = {"value"})
public class ValueActivateExtImpl implements ActivateExt1 {
    public String echo(String msg) {
        return msg;
    }
}

3. 在Resource目录下,添加/META-INF/dubbo/internal/shuqi.dubbotest.spi.activate.ActivateExt1文件,里面的内容

group=shuqi.dubbotest.spi.activate.GroupActivateExtImpl
value=shuqi.dubbotest.spi.activate.ValueActivateExtImpl
order1=shuqi.dubbotest.spi.activate.OrderActivateExtImpl1
order2=shuqi.dubbotest.spi.activate.OrderActivateExtImpl2
shuqi.dubbotest.spi.activate.ActivateExt1Impl1

上车 just do it!

测试一:@Activate注解中声明group

    @Test
    public void testDefault() {
        ExtensionLoader<ActivateExt1> loader = ExtensionLoader.getExtensionLoader(ActivateExt1.class);
        URL url = URL.valueOf("test://localhost/test");
        //查询组为default_group的ActivateExt1的实现
        List<ActivateExt1> list = loader.getActivateExtension(url, new String[]{}, "default_group");
        System.out.println(list.size()); 
        System.out.println(list.get(0).getClass());
    }
1
class shuqi.dubbotest.spi.activate.ActivateExt1Impl1

测试二:@Activate注解中声明多个group

    @Test
    public void test2() {
         URL url = URL.valueOf("test://localhost/test");
        //查询组为group2的ActivateExt1的实现
        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "group2");
        System.out.println(list.size());
        System.out.println(list.get(0).getClass());
    }
1
class shuqi.dubbotest.spi.activate.GroupActivateExtImpl

测试三:@Activate注解中声明了group与value

    @Test
    public void testValue() {
        URL url = URL.valueOf("test://localhost/test");
        //根据   key = value1,group =  value
        //@Activate(value = {"value1"}, group = {"value"})来激活扩展
        url = url.addParameter("value1", "value");
        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "value");  
        System.out.println(list.size());
        System.out.println(list.get(0).getClass());
    }
1
class shuqi.dubbotest.spi.activate.ValueActivateExtImpl

测试四:@Activate注解中声明了order,低的排序优先级搞

    @Test
    public void testOrder() {
        URL url = URL.valueOf("test://localhost/test");
        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "order");
        System.out.println(list.size());
        System.out.println(list.get(0).getClass());
        System.out.println(list.get(1).getClass());
    }
2
class shuqi.dubbotest.spi.activate.OrderActivateExtImpl2
class shuqi.dubbotest.spi.activate.OrderActivateExtImpl1

结论:

从上面的几个测试用例,可以得到下面的结论:1. 根据loader.getActivateExtension中的group和搜索到此类型的实例进行比较,如果group能匹配到,就是我们选择的,也就是在此条件下需要激活的。2. @Activate中的value是参数是第二层过滤参数(第一层是通过group),在group校验通过的前提下,如果URL中的参数(k)与值(v)中的参数名同@Activate中的value值一致或者包含,那么才会被选中。相当于加入了value后,条件更为苛刻点,需要URL中有此参数并且,参数必须有值。3.@Activate的order参数对于同一个类型的多个扩展来说,order值越小,优先级越高。

源码分析

下面我们带着上面的结论,看一下源码。上篇文章我们我们说了ExtensionLoader.getExtensionLoader(ActivateExt1.class)这一步做的,今天我们从他的下一步讲起getActivateExtension

 /**
     * Get activate extensions.
     *
     * @param url    url
     * @param values extension point names
     * @param group  group
     * @return extension list which are activated
     * @see com.alibaba.dubbo.common.extension.Activate
     */
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> exts = new ArrayList<T>();
        /**
         * 将传递过来的values包装成List类型的names
         */
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
        /**
         * 包装好的数据中不包含"-default"
         */
        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
            /**
             * 获取这个类型的数据的所有扩展信息
             */
            getExtensionClasses();
            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
                /**
                 * 获取扩展的名称
                 */
                String name = entry.getKey();
                /**
                 * 获取扩展的注解
                 */
                Activate activate = entry.getValue();
                /**
                 * 判断group是否属于范围
                 *
                 * 1. 如果activate注解的group没有设定,直接返回true
                 * 2. 如果设定了,需要和传入的额group进行比较,看是否
                 * 包含其中,如果包含,返回true
                 *
                 */
                if (isMatchGroup(group, activate.group())) {
                    /**
                     * group 校验通过了,从缓存中获取此name对应的实例
                     */
                    T ext = getExtension(name);
                    /**
                     * names 不包含 遍历此时的name
                     */
                    if (!names.contains(name)
                            /**
                             * names中不包含"-default"
                             */
                            && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
                            /**
                             * 通过URL判断这个activate注解是激活的
                             */
                            && isActive(activate, url)) {
                        /**
                         * 增加扩展
                         */
                        exts.add(ext);
                    }
                }
            }
            /**
             * 按照Activate的方式进行排序,注意order
             */
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        /**
         * 借用usrs这个临时变量,进行循环往exts中塞具体的ext的对象。
         * 如果碰到了"default"就添加到头部,清空usrs这个临时变量。
         * 如果没有"default"那么usrs不会清空,所以下面有个if,说usrs不为空
         * 将里面的内容增加到exts中
         */
        List<T> usrs = new ArrayList<T>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
                    && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
                if (Constants.DEFAULT_KEY.equals(name)) {
                    if (!usrs.isEmpty()) {
                        exts.addAll(0, usrs);
                        usrs.clear();
                    }
                } else {
                    T ext = getExtension(name);
                    usrs.add(ext);
                }
            }
        }
        if (!usrs.isEmpty()) {
            exts.addAll(usrs);
        }
        return exts;
    }

看到了我们属性的方法getExtensionClasses获取这个类型所有的扩展类,随后利用了cachedActivates变量,不知道大家还有没有印象,他的赋值也是再getExtensionClasses方法里面的

Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
    //存在,就往cachedActivates里面添加名称与注解
    cachedActivates.put(names[0], activate);
}

回顾完毕,我们继续说。首先会校验group,对应的方法是isMatchGroup

    /**
     * 判断group是否属于范围
     * <p>
     * 1. 如果activate注解的group没有设定,直接返回true
     * 2. 如果设定了,需要和传入的额group进行比较,看是否
     * 包含其中,如果包含,返回true
     */
    private boolean isMatchGroup(String group, String[] groups) {
        if (group == null || group.length() == 0) {
            return true;
        }
        if (groups != null && groups.length > 0) {
            for (String g : groups) {
                if (group.equals(g)) {
                    return true;
                }
            }
        }
        return false;
    }

如果activate注解的group没有设定,直接返回true,如果设定了,需要和传入的额group进行比较,看是否包含其中,如果包含,返回true,这个点可以看一下测试方法一和二。group 验证完后,根据name获取具体的扩展。随后验证一下是否激活的方法isActive,主要就是根据@Activate注解中的value和URL中的参数的对应做的。

private boolean isActive(Activate activate, URL url) {
        String[] keys = activate.value();
        /**
         * 如果@Activate注解中的value是空的直接返回true
         */
        if (keys.length == 0) {
            return true;
        }
        /**
         * 从activate.value()拿到的数据进行遍历
         */
        for (String key : keys) {
            /**
             * 从URL中获取参数,进行遍历,如果有一个参数同key一致,或者是以.key的方式结尾。
             * 
             */
            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                        && ConfigUtils.isNotEmpty(v)) {
                    return true;
                }
            }
        }
        return false;
    }

如果@Activate注解中的value是空的直接返回true,如果有值,会遍历判断,从URL中获取参数,进行遍历,如果有一个参数同key一致,或者是以.key的方式结尾,并且url中这个k对应的v有有意义的值,同样返回true,说道这里,我们可以看一下测试三,说的就是这个功能。group和value都验证通过后,就可以添加到exts集合中了,随后进行了排序Collections.sort(exts, ActivateComparator.COMPARATOR) 这个可以看一下测试方法四。当所有的过滤条件都通过后,就可以返回了。

相比于@Adaptive来说,@Activate简单一点,处理逻辑也没那么烦乱。

适用场景

主要用在filter上,有的filter需要在provider边需要加的,有的需要在consumer边需要加的,根据URL中的参数指定,当前的环境是provider还是consumer,运行时决定哪些filter需要被引入执行。

测试源码

dubbo-test测试源码

预告,看这里

下一篇: Dubbo SPI 补充知识点-IOC

END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容

  • 1.Block入门 Block是C语言的扩充功能。简单的来说:Block就是带有自动变量(局部变量)的匿名函数。这...
    DDB_CS阅读 526评论 0 0
  • 我是玉丽,一个爱阅读、爱学习、爱分享的90后创业者,致力于推广全民阅读!这是我每天一篇文章的第20篇。 《...
    妈妈成长找玉丽阅读 4,269评论 0 3
  • 今天在家收拾,把那些个没用的全扔了,在清理抽屉时,突然发现一个一丁点大的纸袋,纸袋上有几个稚嫩的字:一个大惊喜。一...
    阳光织成的翅膀阅读 221评论 0 2
  • 一、罗列事实 4月第2周计划完成情况: 制定26项计划,完成20项,达成率77%。 第一优先级8项,完成6项,达成...
    易查理阅读 236评论 0 0
  • 说到第四个习惯,我们的人生便从“独立期”进入到“互赖期”,与周围的人互相牵绊,共同前行。 良好的人...
    冯小斤阅读 398评论 0 2