本文对应源码地址:https://github.com/nieandsun/dubbo-study
注意:
dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外
文章目录
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!!!