天天看點

【dubbo源碼解析】 --- dubbo spi 機制之@Activate簡介1 @Activate标簽的應用場景2 @Activate标簽 及其使用簡介

本文對應源碼位址: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檔案夾下建立一個配置檔案了,内容如下:

【dubbo源碼解析】 --- dubbo spi 機制之@Activate簡介1 @Activate标簽的應用場景2 @Activate标簽 及其使用簡介

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

測試結果如下:

【dubbo源碼解析】 --- dubbo spi 機制之@Activate簡介1 @Activate标簽的應用場景2 @Activate标簽 及其使用簡介

【測試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

測試結果如下:

【dubbo源碼解析】 --- dubbo spi 機制之@Activate簡介1 @Activate标簽的應用場景2 @Activate标簽 及其使用簡介

【測試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

但是并不是,因為中間那個參數指定的要增加或減少實作類,實際上卻并不是,具體原因有興趣的可以翻翻源碼

測試結果如下:

【dubbo源碼解析】 --- dubbo spi 機制之@Activate簡介1 @Activate标簽的應用場景2 @Activate标簽 及其使用簡介

end!!!