【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!!!

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