【dubbo源码解析】 --- dubbo spi 机制之@Activate简介

本文对应源码地址:https://github.com/nieandsun/dubbo-study



1 @Activate标签的应用场景

除了上篇文章《【dubbo源码解析】 — dubbo spi 机制(@SPI、@Adaptive)详解》介绍的内容之外,其实dubbo对SPI机制还进行了一个重要的扩展。

举例来说:在工作中,某种时候存在这样的情形,需要同时启用某个接口的多个实现类,如Filter过滤器。我们希望某种条件下启用这一批实现,而另一种情况下启用那一批实现,比如:希望RPC调用的消费端和服务端,分别启用不同的两批Filter,这该怎么处理呢? —> 这时候dubbo的条件激活注解@Activate,就可以派上用场了。

Activate注解表示一个扩展是否被激活(使用),可以放在类定义和方法(本文不讲)上,dubbo将它标注在spi的扩展类上,表示这个扩展实现激活条件和时机。它有两个设置过滤条件的字段,group,value 都是字符数组。 用来指定这个扩展类在什么条件下激活。


2 @Activate标签 及其使用简介


2.1 简单看一下@Activate标签的源码

首先来看一下@Activate注解的源代码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
   
    String[] group() default {};

    String[] value() default {};
    
    @Deprecated
    String[] before() default {};

    @Deprecated
    String[] after() default {};

    int order() default 0;
}

可以看到@Activate注解主要有五个可选参数,其实before和after标注了@Deprecated ,因此本文也不对这两个可选参数做过多研究了,有兴趣的可以自己试验或查阅资料。


2.2 @Activate标签的使用姿势


2.2.1 定义标有@Activate注解的实现类

先定义一些Filter(org.apache.dubbo.rpc.Filter),并标注上@Activate注解。
注意:该Filter是dubbo的Filter,接口上有@SPI注解 —>即该接口是一个dubbo的SPI扩展点。

【情况一】 @Activate注解里只有group, 表明当调用方只要传递的group中有一个该注解里指定的组员就可以激活该Filter

/***
 * @Activate表示为一个SPI扩展点
 *使用方如果传递了
 * group = CommonConstants.PROVIDER(其实就是字符串”provider“,在dubbo里指提供者)
 *     或 CommonConstants.CONSUMER(其实就是字符串”consumer“,在dubbo里指消费者)
 *     或 字符传”yoyo“
 *     则该Filter被激活
 */
@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER, "yoyo"})
public class FilterA implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("你好,调通了Filer  A实现!");
        return null;
    }
}

【情况二】 @Activate注解里既有group,又有order ,通过@Activate的源码可知,默认情况下order = 0

/**
 * 使用方传递了group = nrsc 则该Filter被激活 ,order表示激活顺序,激活顺序为 0->1->2...
 */
@Activate(group = "nrsc", order = 2)
public class FilterB implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("你好,调通了Filer B实现!");
        return null;
    }
}
/**
 * 使用方传递了group = yoyo 则该Filter被激活 ,order表示激活顺序,激活顺序为 0->1->2...
 */
@Activate(group = "yoyo", order = 3)
public class FilterC implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("你好,调通了Filer C实现!");
        return null;
    }
}
/**
 * 使用方传递了group = yoyo 或group = nrsc 则该Filter被激活 ,order表示激活顺序,激活顺序为 0->1->2...
 */
@Activate(group = {"nrsc", "yoyo"}, order = 4)
public class FilterD implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("你好,调通了Filer D实现!");
        return null;
    }
}

【情况三】 @Activate注解里既有group、order 又有value

/**
 * 使用方传递了group = nrsc或yoyo,并且url中包含MMMM参数,该Filter才能被激活
 */
@Activate(group = {"nrsc", "yoyo"}, order = 1, value = "MMMM")
public class FilterE implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("你好,调通了Filer E实现!");
        return null;
    }
}

2.2.2 在META-INFO/dubbo文件夹下建立配置文件

其次当然是要在META-INFO/dubbo文件夹下建立一个配置文件了,内容如下:
在这里插入图片描述


2.2.3 测试

【测试1】 url里什么参数都不传,但指定group

/**
 * 调用分组为yoyo过滤器
 */
@Test
public void testActivate1() {
    ExtensionLoader<Filter> extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);

    URL url = URL.valueOf("test://localhost/test");
    //第一个参数为url,第二个参数稍后讲,第三个参数为group
    List<Filter> list = extensionLoader.getActivateExtension(url, "", "yoyo");//group
    for (Filter filter : list) {
        filter.invoke(null, null);
    }
}

相信你肯定可以猜到被调用的过滤器及其顺序为: A —> C —> D

测试结果如下:
在这里插入图片描述


【测试2】 url里指定参数MMMM时(注意,参数后面的value不重要,可以是66666也可以是99999,随意指定都ok)的情况

 /**
  * 分组为nrsc
  * url中指定参数MMMM
  */
 @Test
 public void testActivate2() {
     ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);

     URL url = URL.valueOf("test://localhost/test");
     url = url.addParameter("MMMM", "66666");
     List<Filter> list = extensionLoader.getActivateExtension(url, "", "nrsc");
     for (Filter filter : list) {
         filter.invoke(null, null);
     }
 }

这时候你可能会认为只有E过滤器被调用了,其实不是,因为
E过滤器激活的条件是【group 为nrsc或yoyo,且URL中必须有MMMM参数】
但是B、D过滤器的激活条件是只要group为nrsc就ok
所以此时被调用的过滤器及其顺序应该为:E —> B —> D

测试结果如下:
在这里插入图片描述


【测试3】 url里指定参数MMMM且额外指定去除或增加某个,或某几个实现类的情况

    /**
     * 分组为nrsc
     * url中有参数MMMM
     * url中指定要使用a,去除c实现
     */
    @Test
    public void testActivate3() {
        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);

        URL url = URL.valueOf("test://localhost/test");
        url = url.addParameter("MMMM", "7777");
        //url = url.addParameter("myfilter", "+b,-a,-d"); 和下面的含义一样
        url = url.addParameter("myfilter", "b,-a,-d");

        //中间的参数用来指定额外去除或增加哪个实现类
        List<Filter> list = extensionLoader.getActivateExtension(url, "myfilter", "yoyo");
        for (Filter filter : list) {
            filter.invoke(null, null);
        }
    }

相信看了上面的注释,你可能会再结合一下order的取值,认为此时被调用的过滤器及其顺序应该为:E —> B —> C
但是并不是,因为中间那个参数指定的要增加或减少实现类,实际上却并不是,具体原因有兴趣的可以翻翻源码

测试结果如下:
在这里插入图片描述


end!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章