本文對應源碼位址:https://github.com/nieandsun/dubbo-study 注意:
dubbo 要求SPI擴充點的實作類必須要有一個無參構造,除了Wrapper實作類之外
文章目錄
- 1 @Activate标簽的應用場景
- 2 @Activate标簽 及其使用簡介
-
- 2.1 簡單看一下@Activate标簽的源碼
- 2.2 @Activate标簽的使用姿勢
-
- 2.2.1 定義标有@Activate注解的實作類
- 2.2.2 在META-INFO/dubbo檔案夾下建立配置檔案
- 2.2.3 測試
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
測試結果如下:
url裡指定參數MMMM時(
【測試2】
)的情況
注意,參數後面的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!!!