千家信息网

Dubbo的SPI机制介绍以及Activate的使用方法

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,这篇文章主要介绍"Dubbo的SPI机制介绍以及Activate的使用方法",在日常操作中,相信很多人在Dubbo的SPI机制介绍以及Activate的使用方法问题上存在疑惑,小编查阅了各式资料,整理
千家信息网最后更新 2025年02月03日Dubbo的SPI机制介绍以及Activate的使用方法

这篇文章主要介绍"Dubbo的SPI机制介绍以及Activate的使用方法",在日常操作中,相信很多人在Dubbo的SPI机制介绍以及Activate的使用方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Dubbo的SPI机制介绍以及Activate的使用方法"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1、Dubbo的@Activate例子

@SPIpublic interface ActivateExt {    String echo(String msg);}
@Activate(group = {"default"})public class ActivateExtImpl1 implements ActivateExt {    @Override    public String echo(String msg) {        return msg;    }}@Activate(group = {"group1", "group2"})public class GroupActivateExtImpl implements ActivateExt {    @Override    public String echo(String msg) {        return msg;    }}
@Activate(order = 2, group = {"order"})public class OrderActivateExtImpl1 implements ActivateExt {    @Override    public String echo(String msg) {        return msg;    }}@Activate(order = 1, group = {"order"})public class OrderActivateExtImpl2 implements ActivateExt {    @Override    public String echo(String msg) {        return msg;    }}
@Activate(value = {"value"}, group = {"group"})public class ValueActivateExtImpl implements ActivateExt {    @Override    public String echo(String msg) {        return msg;    }}

resources下新建META-INF/dubbo/internal文件夹,新建com.alibaba.dubbo.demo.provider.activate.ActivateExt,即接口的全限定名,文件内容为:

group=com.alibaba.dubbo.demo.provider.activate.impl.GroupActivateExtImplkey=com.alibaba.dubbo.demo.provider.activate.impl.ValueActivateExtImplorder1=com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1order2=com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl2com.alibaba.dubbo.demo.provider.activate.impl.ActivateExtImpl1

下面是几个单元测试及其测试结果,观察测试结果我们可以得出以下结论:

  1. 如果loader.getActivateExtension(URL url, String[] values, String group)中的group参数能够匹配到,就是我们要选择的,test1中根据group=default匹配到了ActivateExtImpl1,它也是在此条件下需要激活的

  2. @Activate中的value是第二层校验参数,第一层是group,在group校验通过的情况下,如果URL中的key参数与类@Activate注解中的value参数一致且key参数对应的值不为空,则才会被选中

  3. order越小,优先级越高

/** * 1 * class com.alibaba.dubbo.demo.provider.activate.impl.ActivateExtImpl1 */@Testpublic void test1(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);    URL url = URL.valueOf("test://localhost/test");    List list = loader.getActivateExtension(url, new String[]{}, "default");    System.out.println(list.size());    list.forEach(item -> System.out.println(item.getClass()));}
/** * 1 * class com.alibaba.dubbo.demo.provider.activate.impl.GroupActivateExtImpl */@Testpublic void test2(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);    URL url = URL.valueOf("test://localhost/test");    List list = loader.getActivateExtension(url, new String[]{}, "group1");    System.out.println(list.size());    list.forEach(item -> System.out.println(item.getClass()));}
/** * 2 * class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1 * class com.alibaba.dubbo.demo.provider.activate.impl.ValueActivateExtImpl */@Testpublic void test3(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);    URL url = URL.valueOf("test://localhost/test");    // 注意这里要使用url接收,不能直接url.addParameter()    url = url.addParameter("value", "test");    List list = loader.getActivateExtension(url, new String[]{"order1", "default"}, "group");    System.out.println(list.size());    list.forEach(item -> System.out.println(item.getClass()));}
/** * 2 * class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl2 * class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1 */@Testpublic void test4(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);    URL url = URL.valueOf("test://localhost/test");    List list = loader.getActivateExtension(url, new String[]{}, "order");    System.out.println(list.size());    list.forEach(item -> System.out.println(item.getClass()));}

2、源码分析

public void test3(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);    URL url = URL.valueOf("test://localhost/test");    // 注意这里要使用url接收,不能直接url.addParameter()    url = url.addParameter("value", "test");    List list = loader.getActivateExtension(url, new String[]{"order1", "default"}, "group");    System.out.println(list.size());    list.forEach(item -> System.out.println(item.getClass()));}
public List getActivateExtension(URL url, String[] values, String group) {    List exts = new ArrayList();    // 将传递进来的values包装成List类型的names    List names = values == null ? new ArrayList(0) : Arrays.asList(values);    // 包装好的数据中不含"-default"    if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {        // 获取这个类型的数据的所有扩展信息        getExtensionClasses();        for (Map.Entry entry : cachedActivates.entrySet()) {            // 获取拓展的名称,在配置文件中配置的key=value中的key值            String name = entry.getKey();            // 获取注解            Activate activate = entry.getValue();            // 判断group是否匹配,group是程序传进来的值,如果没有设定则返回true表示匹配,            // 如果设定了需要进行比较匹配            if (isMatchGroup(group, activate.group())) {                // 获取key值即name对应的实例                T ext = getExtension(name);                if (!names.contains(name)                        && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)                        && isActive(activate, url)) {                    exts.add(ext);                }            }        }        // 排序        Collections.sort(exts, ActivateComparator.COMPARATOR);    }    List usrs = new ArrayList();    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)) {                // 如果name是default,就将usrs加入exts头部并清空usrs                if (!usrs.isEmpty()) {                    exts.addAll(0, usrs);                    usrs.clear();                }            } else {                // 获取name对应的拓展并加入usrs                T ext = getExtension(name);                usrs.add(ext);            }        }    }    if (!usrs.isEmpty()) {        exts.addAll(usrs);    }    return exts;}

上述代码中用到的cachedActivates是在getExtensionClasses()方法中赋值的,具体可以参考: Dubbo的SPI机制分析1-SPI加载class

Activate activate = clazz.getAnnotation(Activate.class);if (activate != null) {    cachedActivates.put(names[0], activate);}
// 判断group是否匹配,很好理解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;}
// 将url中的键key与Activate注解中的value值做匹配,并且key所对应的value值不能为空private boolean isActive(Activate activate, URL url) {    String[] keys = activate.value();    if (keys.length == 0) {        return true;    }    for (String key : keys) {        for (Map.Entry 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;}

注意,如果是以下这种形式,传入的values中有-default,则所有的匹配可以通过的@Activate都不会被激活,只有values中指定的可以被激活。如果传入了"-"符合开头的扩展类名,则该扩展点也不会被激活。

// 输出class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);URL url = URL.valueOf("test://localhost/test");// 注意这里要使用url接收,不能直接url.addParameter()url = url.addParameter("value", "test");List list = loader.getActivateExtension(url, new String[]{"order1", "-default"}, "group");System.out.println(list.size());list.forEach(item -> System.out.println(item.getClass()));

到此,关于"Dubbo的SPI机制介绍以及Activate的使用方法"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0