为了账号安全,请及时绑定邮箱和手机立即绑定

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;

@SPIpublic 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());
    }
1class 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());
    }
1class 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());
    }
1class 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());
    }
2class shuqi.dubbotest.spi.activate.OrderActivateExtImpl2class 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需要被引入执行。



作者:数齐
链接:https://www.jianshu.com/p/bc523348f519


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消