本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外
除了上篇文章《【dubbo源码解析】 — dubbo spi 机制(@SPI、@Adaptive)详解》介绍的内容之外,其实dubbo对SPI机制还进行了一个重要的扩展。
举例来说:在工作中,某种时候存在这样的情形,需要同时启用某个接口的多个实现类,如Filter过滤器。我们希望某种条件下启用这一批实现,而另一种情况下启用那一批实现,比如:希望RPC调用的消费端和服务端,分别启用不同的两批Filter,这该怎么处理呢? —> 这时候dubbo的条件激活注解@Activate,就可以派上用场了。
Activate注解表示一个扩展是否被激活(使用),可以放在类定义和方法(本文不讲)上,dubbo将它标注在spi的扩展类上,表示这个扩展实现激活条件和时机。它有两个设置过滤条件的字段,group,value 都是字符数组。 用来指定这个扩展类在什么条件下激活。
首先来看一下@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 ,因此本文也不对这两个可选参数做过多研究了,有兴趣的可以自己试验或查阅资料。
先定义一些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; } }其次当然是要在META-INFO/dubbo文件夹下建立一个配置文件了,内容如下:
【测试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!!!