第3章 Dubbo SPI 使用姿勢

SPI 機制是實現(xiàn)可擴展性的一種方式尺锚。上一篇介紹了 JDK SPI 的使用姿勢和基本原理英上,本節(jié)來分析 Dubbo SPI 的基本使用、適配類使用眉撵、AOP 使用帐要、IOC 使用以及激活點的使用(基于 dubbo 2.6.6)把敞。

示例代碼地址:https://github.com/zhaojigang/dubbo-demo

可參考的 SPI 實現(xiàn):

一、Dubbo SPI 基本使用

image.png
  • Dubbo 配置文件名依然是:SPI 接口的全接口名
  • Dubbo SPI 會從以下三個目錄讀取配置文件:
  • META-INF/dubbo/internal/:該目錄用于存儲 Dubbo 框架本身提供的 SPI 擴展實現(xiàn)榨惠,eg.
    image.png
  • META-INF/dubbo/:第三方提供的擴展(包括我們自己寫的)建議 寫在這個目錄下(實際上寫到三個目錄的任一目錄下都可以奋早,但是不方便管理)
  • META-INF/services/:JDK SPI 的配置文件目錄

SPI 接口

@SPI("logback")
public interface Log {
    void execute();
}

SPI 接口實現(xiàn)

public class Logback implements Log {
    @Override
    public void execute() {
        System.out.println("this is logback!");
    }
}

public class Log4j implements Log {
    @Override
    public void execute() {
        System.out.println("this is log4j!");
    }
}

配置文件

logback=io.study.dubbo.spi.basic.Logback
log4j=io.study.dubbo.spi.basic.Log4j

測試主類

public class TestBasic {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

        // 1. 指定名稱獲取具體 SPI 實現(xiàn)類
        Log logback = loader.getExtension("logback");
        logback.execute(); // this is logback!
        Log log4j = loader.getExtension("log4j");
        log4j.execute(); // this is log4j!

        // 2. 獲取默認實現(xiàn)類 @SPI("logback") 中的 logback 就指定了默認的 SPI 實現(xiàn)類的 key
        Log defaultExtension = loader.getDefaultExtension();
        defaultExtension.execute(); // this is logback!
        System.out.println(loader.getDefaultExtensionName()); // logback

        // 3. 獲取支持哪些 SPI 實現(xiàn)類
        Set<String> supportedExtensions = loader.getSupportedExtensions();
        supportedExtensions.forEach(System.out::println); // log4j \n logback

        // 4. 獲取已經(jīng)加載了哪些 SPI 實現(xiàn)類
        Set<String> loadedExtensions = loader.getLoadedExtensions();
        loadedExtensions.forEach(System.out::println); // log4j \n logback

        // 5. 根據(jù) SPI 實現(xiàn)類實例或者實現(xiàn)類的 Class 信息獲取其 key
        System.out.println(loader.getExtensionName(logback)); // logback
        System.out.println(loader.getExtensionName(Logback.class)); // logback

        // 6. 判斷是否具有指定 key 的 SPI 實現(xiàn)類
        System.out.println(loader.hasExtension("logback")); // true
        System.out.println(loader.hasExtension("log4j2"));  // false
    }
}

二盛霎、Dubbo SPI 適配類使用

  • Dubbo 適配類:適配類其實就是一個工廠類,根據(jù)傳遞的參數(shù)動態(tài)的使用相應(yīng)的 SPI 實現(xiàn)類耽装;
  • Dubbo 適配類有兩種姿勢:(一個 SPI 接口最多只有一個適配類愤炸,如果有手動編寫的適配類,那么則首先使用手動編寫的適配類)
  • 手動編寫一個適配類(Dubbo 默認只提供了兩個手動編寫的適配類 AdaptiveExtensionFactoryAdaptiveCompiler
  • 根據(jù) SPI 接口動態(tài)生成一個適配類

2.1 手動編寫一個適配類

SPI 接口

@SPI("logback")
public interface Log {
    void execute(String name);
}

SPI 實現(xiàn)

public class Log4j implements Log {
    @Override
    public void execute(String name) {
        System.out.println("this is log4j! " + name);
    }
}

public class Logback implements Log {
    @Override
    public void execute(String name) {
        System.out.println("this is logback! " + name);
    }
}

SPI適配類

/**
 * 手動編寫 SPI 適配類
 * 注意:適配類也需要在配置文件中進行配置
 */
@Adaptive
public class AdaptiveLog implements Log {
    private static final ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

    @Override
    public void execute(String name) {
        Log log = null;
        if (name == null || name.length() == 0) {
            log = loader.getDefaultExtension();
        } else {
            log = loader.getExtension(name);
        }
        if (log != null) {
            log.execute(name);
        }
    }
}

適配類要實現(xiàn) SPI 接口掉奄。(見 ExtensionLoader<T>.public T getAdaptiveExtension() 方法定義规个,類泛型與方法返回泛型相同,都是 SPI 接口)

配置文件

log4j=io.study.dubbo.spi.adaptive.manual.Log4j
logback=io.study.dubbo.spi.adaptive.manual.Logback
adaptive=io.study.dubbo.spi.adaptive.manual.AdaptiveLog

注意:手動編寫的適配類需要在配置文件中進行配置

測試主類

public class TestAdaptiveManual {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

        System.out.println("======================= 獲取 SPI 適配類(自己手寫適配類) =======================");
        Log adaptiveExtension = loader.getAdaptiveExtension(); // AdaptiveLog 實例
        adaptiveExtension.execute("log4j"); // this is log4j! log4j
    }
}

2.2 根據(jù) SPI 接口動態(tài)生成一個適配類

SPI 接口

/**
 * SPI 接口
 */
@SPI("logback")
public interface Log {

    /**
     * 含有 @Adaptive 注解的方法姓建,生成的動態(tài)類會實現(xiàn)該方法绰姻,該方法必須直接包含 URL 參數(shù)或者方法的參數(shù)中要包含 URL 參數(shù)
     * @Adaptive 注解中的 String[] value() 代表 url 中用于獲取 SPI 實現(xiàn)類的 key 的參數(shù)名:
     *
     * eg. 本例的配置生成的代碼如下
     * String extName = url.getParameter("xxx", url.getParameter("ooo", "logback")); // 其中 logback 是默認值, 即先獲取 Url 中key為xxx的值,如果該值存在引瀑,則使用該值去 SPI 配置文件中獲取對應(yīng)的實現(xiàn)
     * Log extension = ExtensionLoader.getExtensionLoader(Log.class).getExtension(extName);
     */
    @Adaptive({"xxx","ooo"})
    void execute(URL url);

    /**
     * 不帶有 @Adaptive 注解的方法狂芋,生成的動態(tài)類中該方法的方法體直接拋異常
     */
    void test();
}
注意點
  • 含有 @Adaptive 注解的方法,生成的動態(tài)類會實現(xiàn)該方法憨栽,該方法必須直接包含 URL 參數(shù)或者方法的參數(shù)中要包含 URL 參數(shù)(因為要根據(jù) URL 參數(shù)來判斷具體使用哪一個 SPI 實現(xiàn)帜矾,具體見如下“動態(tài)生成的適配類”,由此可見屑柔,適配類其實就是一個工廠類屡萤,根據(jù)傳遞的參數(shù)動態(tài)的使用相應(yīng)的 SPI 實現(xiàn)類)
  • 不帶有 @Adaptive 注解的方法,生成的動態(tài)類中該方法的方法體直接拋異常
  • @Adaptive 注解中的 String[] value() 代表 url 中用于獲取 SPI 實現(xiàn)類的 key 的參數(shù)名(示例解釋見如下代碼注釋)掸宛;假設(shè) @Adaptive 沒有配置 String[] value()死陆,那么會默認按照類名(大寫變小寫,且加“.”作為分隔符)作為 key 去查找(eg. interface io.study.TestLog唧瘾,則 key=“test.log”)措译,即 String extName = url.getParameter("test.log", "logback");
動態(tài)生成的適配類
public class Log$Adaptive implements io.study.dubbo.spi.adaptive.auto.Log {
    @Override
    public void execute(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) {
            throw new IllegalArgumentException("url == null");
        }
        com.alibaba.dubbo.common.URL url = arg0;
        // 首先獲取url中的xxx=ppp這個參數(shù)的值ppp,假設(shè)有饰序,使用該值去獲取key為ppp的 SPI 實現(xiàn)類领虹;假設(shè)沒有,再獲取ooo=ppp求豫,假設(shè)也沒有塌衰,使用默認的logback去獲取key為logback的SPI實現(xiàn)類
        String extName = url.getParameter("xxx", url.getParameter("ooo", "logback"));
        if (extName == null) {
            throw new IllegalStateException("Fail to get extension(io.study.dubbo.spi.adaptive.auto.Log) name from url(" + url.toString() + ") use keys([xxx, ooo])");
        }
        io.study.dubbo.spi.adaptive.auto.Log extension = (io.study.dubbo.spi.adaptive.auto.Log) ExtensionLoader.getExtensionLoader(io.study.dubbo.spi.adaptive.auto.Log.class).getExtension(extName);
        extension.execute(arg0);
    }

    @Override
    public void test() {
        throw new UnsupportedOperationException("method public abstract void io.study.dubbo.spi.adaptive.auto.Log.test() of interface io.study.dubbo.spi.adaptive.auto.Log is not adaptive method!");
    }
}

SPI 實現(xiàn)

public class Log4j implements Log {
    @Override
    public void execute(URL url) {
        System.out.println("this is log4j! " + url.getIp());
    }

    @Override
    public void test() {}
}

public class Logback implements Log {
    @Override
    public void execute(URL url) {
        System.out.println("this is logback! " + url.getIp());
    }

    @Override
    public void test() {}
}

配置文件

logback=io.study.dubbo.spi.adaptive.auto.Logback
log4j=io.study.dubbo.spi.adaptive.auto.Log4j

測試主類

public class TestAdaptiveAuto {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

        Log adaptiveExtension = loader.getAdaptiveExtension();
        URL url = new URL("dubbo", "10.211.55.6", 8080);
        adaptiveExtension.execute(url.addParameter("xxx", "log4j")); // this is log4j! 10.211.55.6
    }
}

三、Dubbo SPI AOP 使用

相較于 JDK SPI 的一個增強點蝠嘉。如果設(shè)置了 Wrapper 類最疆,該類會對所有的 SPI 實現(xiàn)類進行包裹。

SPI 接口

@SPI("logback")
public interface Log {
    void execute();
}

SPI 實現(xiàn)

public class Log4j implements Log {
    @Override
    public void execute() {
        System.out.println("this is log4j!");
    }
}

public class Logback implements Log {
    @Override
    public void execute() {
        System.out.println("this is logback!");
    }
}

wrapper 類

/**
 * wrapper 類也必須實現(xiàn) SPI 接口蚤告,否則 loadClass() 處報錯
 */
public class LogWrapper1 implements Log {
    private Log log;

    /**
     * wrapper 類必須有一個含有單個 Log 參數(shù)的構(gòu)造器
     */
    public LogWrapper1(Log log) {
        this.log = log;
    }

    @Override
    public void execute() {
        System.out.println("LogWrapper1 before");
        log.execute();
        System.out.println("LogWrapper1 after");
    }
}

public class LogWrapper2 implements Log {
    private Log log;
    public LogWrapper2(Log log) {
        this.log = log;
    }

    @Override
    public void execute() {
        System.out.println("LogWrapper2 before");
        log.execute();
        System.out.println("LogWrapper2 after");
    }
}
  • wrapper 類也必須實現(xiàn) SPI 接口
  • wrapper 類必須有一個含有單個 Log 參數(shù)的構(gòu)造器

配置文件

log4j=io.study.dubbo.spi.aop.Log4j
logback=io.study.dubbo.spi.aop.Logback
io.study.dubbo.spi.aop.LogWrapper1
io.study.dubbo.spi.aop.LogWrapper2
  • wrapper 類必須在配置文件中進行配置努酸,不需要配置 key

測試主類

public class TestAOP {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

        System.out.println("======================= 根據(jù)指定名稱獲取具體的 SPI 實現(xiàn)類(測試 wrapper) =======================");
        Log logback = loader.getExtension("logback"); // 最外層的 Wrapper 類實例
        /**
         * 輸出
         * LogWrapper2 before
         * LogWrapper1 before
         * this is logback!
         * LogWrapper1 after
         * LogWrapper2 after
         */
        logback.execute();
    }
}

關(guān)于 wrapper 類的加載順序,見 https://github.com/apache/dubbo/issues/4578

四罩缴、Dubbo SPI IOC 使用

也是對 JDK SPI 功能的一個增強蚊逢,

  • Dubbo SPI IOC 有兩種姿勢:
  • 注入 Dubbo 適配類:值得注意的是层扶,Dubbo 只能注入適配類,不能直接注入 SPI 具體實現(xiàn)烙荷;
  • 注入 Spring Bean:需要將包含該 Spring Bean 的 Spring 上下文添加到 Dubbo 的 Spring 上下文管理器(SpringExtensionFactory)中镜会,這樣后續(xù)做 IOC 時,才能獲取到該 Bean
  • Dubbo 的 SPI 使用的是 set 注入终抽,所以需要提供一個 public void setXxx(單個 SPI 接口參數(shù)) 方法戳表,對于注入適配類方式來講,由于注入的只是適配類昼伴,只與 SPI 接口有關(guān)匾旭,與 setXxx 方法的 Xxx無關(guān);對于注入 Spring Bean 方式來講圃郊,由于注入的是具體的 Bean价涝,Xxx 是 Spring Bean 的名稱。

2.1 注入 Dubbo 適配類

SPI 接口

@SPI("logback")
public interface Log {
    void execute();
}

SPI 實現(xiàn)

public class Logback implements Log {

    /**
     * SPI IOC 注入:
     * Book 是 SPI 接口持舆,
     * 必須存在一個 public ooo setXxx(單個SPI接口) 的方法才可以進行 IOC 注入色瘩,
     * 且被注入的 SPI 接口必須有適配類(無論是手動還是自動)
     */
    private Book book;

    /**
     * 對于 SPI 注入方式來講,setXxx 中的 Xxx 沒有任何作用逸寓,因為注入的都是 SPI 接口的適配類而不是具體的實現(xiàn)類
     */
    public void setBookx(Book book) {
        this.book = book;
    }

    @Override
    public void execute() {
        URL url = new URL("dubbo", "10.211.55.5", 8080);
        System.out.println("this is logback! " + book.bookName(url.addParameter("language", "go")));
    }
}

public class Log4j implements Log {

    /**
     * SPI IOC 注入:
     * Book 是 SPI 接口居兆,
     * 必須存在一個 public ooo setXxx(單個SPI接口) 的方法才可以進行 IOC 注入,
     * 且被注入的 SPI 接口必須有適配類(無論是手動還是自動)
     */
    private Book book;

    // @DisableInject 禁用 IOC 注入
    @DisableInject
    public void setBook(Book book) {
        this.book = book;
    }

    @Override
    public void execute() {
        System.out.println("this is log4j!");
    }
}

被注入的 SPI 接口及其實現(xiàn)類

/**
 * SPI IOC 注入方式:必須有適配類(無論是手動還是自動)
 * note:手動編寫的 Adaptive 類內(nèi)也可以實現(xiàn) IOC 注入
 */
@SPI("java")
public interface Book {
    @Adaptive({"language"})
    String bookName(URL url);
}

public class JavaBook implements Book {
    @Override
    public String bookName(URL url) {
        return "this is java book竹伸!" + url.getIp();
    }
}

public class GoBook implements Book {
    @Override
    public String bookName(URL url) {
        return "this is go book泥栖!" + url.getIp();
    }
}

// 動態(tài)生成的 SPI 適配類
public class Book$Adaptive implements io.study.dubbo.spi.ioc.spi.Book {
    @Override
    public java.lang.String bookName(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) {
            throw new IllegalArgumentException("url == null");
        }
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = url.getParameter("language", "java");
        if (extName == null) {
            throw new IllegalStateException("Fail to get extension(io.study.dubbo.spi.ioc.spi.Book) name from url(" + url.toString() + ") use keys([language])");
        }
        io.study.dubbo.spi.ioc.spi.Book extension = (io.study.dubbo.spi.ioc.spi.Book) ExtensionLoader.getExtensionLoader(io.study.dubbo.spi.ioc.spi.Book.class).getExtension(extName);
        return extension.bookName(arg0);
    }
}

配置文件

Log SPI 配置文件
log4j=io.study.dubbo.spi.ioc.spi.Log4j
logback=io.study.dubbo.spi.ioc.spi.Logback
Book SPI 配置文件
java=io.study.dubbo.spi.ioc.spi.JavaBook
go=io.study.dubbo.spi.ioc.spi.GoBook

測試主類

public class TestSPIIOC {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);

        // 1. 測試 SPI IOC
        Log logback = loader.getExtension("logback");
        logback.execute(); // this is logback! this is go book!10.211.55.5

        // 2. 測試禁用 SPI IOC
        Log log4j = loader.getExtension("log4j");
        log4j.execute(); // this is log4j!
    }
}

2.2 注入 Spring Bean

SPI 接口

@SPI("logback")
public interface Log {
    void execute();
}

SPI 實現(xiàn)

public class Logback implements Log {
    /**
     * Spring IOC 注入:
     * 1. 必須存在一個 public ooo setXxx(其中 Xxx 是 Spring bean 的名稱) 的方法才可以進行 IOC 注入勋篓,
     */
    private Book book;

    /**
     * 對于 Spring 注入來講吧享,setXxx 中的 Xxx 代表了注入 Bean 的名稱,這里注入的就是 javaBook 這個 Bean
     */
    public void setJavaBook(Book book) {
        this.book = book;
    }

    @Override
    public void execute() {
        System.out.println("this is logback! " + book.bookName());
    }
}

public class Log4j implements Log {
    /**
     * Book 是 SPI 接口生巡,
     * 必須存在一個 public ooo setXxx(單個SPI接口) 的方法才可以進行 IOC 注入
     */
    private Book book;

    public void setGoBook(Book book) {
        this.book = book;
    }

    @Override
    public void execute() {
        System.out.println("this is log4j!" + book.bookName());
    }
}

被注入的 SPI 接口及其實現(xiàn)類

/**
 * Spring IOC 注入方式
 */
public interface Book {
    String bookName();
}

public class GoBook implements Book {
    @Override
    public String bookName() {
        return "this is go book耙蔑!";
    }
}

public class JavaBook implements Book {
    @Override
    public String bookName() {
        return "this is java book!";
    }
}

spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <!-- spring bean -->
    <bean id="javaBook" class="io.study.dubbo.spi.ioc.spring.JavaBook"/>
    <bean id="goBook" class="io.study.dubbo.spi.ioc.spring.GoBook"/>
</beans>

SPI 配置文件

log4j=io.study.dubbo.spi.ioc.spring.Log4j
logback=io.study.dubbo.spi.ioc.spring.Logback

測試主類

public class TestSpringIOC {
    public static void main(String[] args) {
        // 1. 創(chuàng)建 spring 容器 + 加載 Bean 到該容器中
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring.xml"});
        // 2. 將 spring 容器添加到 dubbo 的 SpringExtensionFactory 工廠中
        SpringExtensionFactory.addApplicationContext(context);

        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);
        // 3. 測試 dubbo spring ioc
        Log logback = loader.getExtension("logback");
        logback.execute();  // this is logback! this is java book孤荣!
    }
}

五、Dubbo SPI 激活點使用

Dubbo 的激活點機制基于 @Activate 注解完成须揣,可以用于實現(xiàn)根據(jù)條件加載多個 SPI 激活點實現(xiàn)類盐股。

  • @Activate 注解:
  • String[] group() default {}
  • 如果 getActivateExtension 接口傳入的 group 參數(shù)為 null 或者 length==0,表示不限制 group耻卡,則允許加載當(dāng)前 SPI 實現(xiàn)疯汁;
  • 查看當(dāng)前的 SPI 實現(xiàn)的 @Activate 注解中的參數(shù) groups 是否包含傳入的限制參數(shù) group,如果包含卵酪,則允許加載當(dāng)前的 SPI 實現(xiàn)幌蚊。
  • String[] value() default {}
  • 如果當(dāng)前的 SPI 實現(xiàn)的 @Activate 注解沒有 value() 屬性谤碳,則認為默認是允許當(dāng)前的 SPI 實現(xiàn)加載的;
  • 如果 value() 中的任一值出現(xiàn)在當(dāng)前傳入的 URL#getParameters() 中的一個參數(shù)名溢豆,則認為默認是允許當(dāng)前的 SPI 實現(xiàn)加載的蜒简;
  • 激活點加載流程(僅列出最常用的主線,其他支線見后續(xù)的源碼分析及源碼注釋漩仙,整個流程配合下邊的例子 TestActivate#testValue() 來看):
  1. 首先獲取除了傳入的 spiKey 集合(values)指定的 spi 激活點實現(xiàn)類(稱為 default 激活點)搓茬,之后對 default 激活點進行排序

加載 default 激活點的規(guī)則:

  • 如果 getActivateExtension 接口傳入的 group 參數(shù)為 null 或者 length==0,表示不限制 group队他,則允許加載當(dāng)前 SPI 實現(xiàn)卷仑;
  • 如果 group 有效,則查看當(dāng)前的 SPI 實現(xiàn)的 @Activate 注解中的參數(shù) groups 是否包含傳入的限制參數(shù) group麸折,如果包含锡凝,則允許加載當(dāng)前的 SPI 實現(xiàn);
  • 傳入的spiKey 集合(values)不包含(-name垢啼,name 表示當(dāng)前處理到的 SPI 激活點的 spiKey):也就是說配置 -name 可以排除掉某個實現(xiàn)類私爷;
  • 如果當(dāng)前的 SPI 實現(xiàn)的 @Activate 注解沒有 value() 屬性,則認為默認是加載的膊夹,直接返回 true衬浑;
  • 如果當(dāng)前的 SPI 實現(xiàn)的 @Activate 注解有 value() 屬性,遍歷每一個元素放刨,如果 url.getParameters() 中的參數(shù)名包含了其中任意一個元素(也就是說String[] value() 中的任一值出現(xiàn)在當(dāng)前傳入的 URL#parameters() 中的一個參數(shù)名)
  1. 之后獲取傳入的 spiKey 集合(values)指定的 SPI 激活點實現(xiàn)類(稱為 usr 激活點)
  • 傳入的spiKey 集合(values)不包含(-name工秩,name 表示當(dāng)前處理到的 SPI 激活點的 spiKey):也就是說配置 -name 可以排除掉某個實現(xiàn)類;
  1. 將 default 激活點集合和 usr 激活點集合放到一個集合中进统,default 在前助币,usr 在后

SPI 接口

@SPI
public interface Log {
    void execute();
}

SPI 實現(xiàn)

@Activate
public class NoCondition implements Log {
    @Override
    public void execute() {
        System.out.println("this is noCondition!");
    }
}

// order 值越小,則排在集合前邊螟碎,order 默認為 0
@Activate(group = {"provider"}, order = 1)
public class SingleGroup implements Log {
    @Override
    public void execute() {
        System.out.println("this is single group!");
    }
}

@Activate(group = {"provider", "consumer"}, order = 2)
public class MultiGroup implements Log {
    @Override
    public void execute() {
        System.out.println("this is multi group!");
    }
}

@Activate(value = {"singleValue"})
public class SingleValue implements Log {
    @Override
    public void execute() {
        System.out.println("this is single value!");
    }
}

@Activate(value = {"multi"})
public class MultiValue implements Log {
    @Override
    public void execute() {
        System.out.println("this is multi value!");
    }
}

@Activate(group = {"provider", "consumer"}, value = {"groupAndValue"})
public class GroupAndValue implements Log {
    @Override
    public void execute() {
        System.out.println("this is GroupAndValue!");
    }
}

配置文件

nc=io.study.dubbo.spi.activate.NoCondition
sg=io.study.dubbo.spi.activate.SingleGroup
mg=io.study.dubbo.spi.activate.MultiGroup
sv=io.study.dubbo.spi.activate.SingleValue
mv=io.study.dubbo.spi.activate.MultiValue
gv=io.study.dubbo.spi.activate.GroupAndValue

測試主類

public class TestActivate {
    public static void main(String[] args) {
        ExtensionLoader<Log> loader = ExtensionLoader.getExtensionLoader(Log.class);
        testGroup(loader);
        testValue(loader);
    }

    /**
     * 1. 測試 group
     * 僅僅過濾出@Activate.groups包含url傳入的group=xxx參數(shù)
     */
    private static void testGroup(ExtensionLoader<Log> loader) {
        System.out.println("======================= 測試 group =======================");
        URL url = new URL("dubbo", "10.211.55.6", 8080);
        String group = "provider";
        String[] values = new String[]{};
        List<Log> activateExtension = loader.getActivateExtension(url, values, group);
        /**
         * 輸出:
         * this is single group!
         * this is multi group!
         */
        activateExtension.forEach(Log::execute);
    }

    /**
     * 2. 測試 value
     */
    private static void testValue(ExtensionLoader<Log> loader) {
        System.out.println("======================= 測試 value =======================");
        URL url = new URL("dubbo", "10.211.55.6", 8080);
//        url = url.addParameter("groupAndValue", "gv");
        String[] values = new String[]{"sv", "-mg"};
        /**
         * NoCondition @Activate                                    no
         * SingleGroup @Activate(group = {"provider"}, order = 1)   sg
         * MultiGroup  @Activate(group = {"provider", "consumer"}, order = 2)  mg
         * SingleValue @Activate(value = {"singleValue"})           sv
         * MultiValue  @Activate(value = {"multi"})                 mv
         * GroupAndValue @Activate(group = {"provider", "consumer"}, value = {"groupAndValue"})  gv
         *
         * 1.首先加載 default 激活點(除了 "sv", "mg"之外的其他激活點)眉菱,加載條件:
         *   我們加了 group 參數(shù),首先會獲取具有相關(guān) group 的組掉分,這里獲取到 SingleGroup俭缓、GroupAndValue,
         *   由于 SingleGroup 沒有配置 values 屬性酥郭,所以認為激活华坦,而 GroupAndValue 的 value 值的任一元素(groupAndValue)沒有出現(xiàn)在 url.getParameter中,
         *   所以 GroupAndValue 不能加載(如果加上該句 url = url.addParameter("groupAndValue", "gv"); 代碼不从,則可以加載)
         *   最后對所有的 default 激活點按照 order惜姐、before、after 屬性進行排序
         * 2.之后加載 usr 激活點("sv", "mg" 激活點)椿息,sv 正常加載歹袁,而 mg 我們配置的 values 是 -mg坷衍,也就是說不加載 mg
         */
        List<Log> activateExtension = loader.getActivateExtension(url, values, "provider");
        /**
         * 輸出:
         * this is single group!
         * this is single value!
         */
        activateExtension.forEach(Log::execute);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市条舔,隨后出現(xiàn)的幾起案子枫耳,更是在濱河造成了極大的恐慌,老刑警劉巖逞刷,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘉涌,死亡現(xiàn)場離奇詭異,居然都是意外死亡夸浅,警方通過查閱死者的電腦和手機仑最,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帆喇,“玉大人警医,你說我怎么就攤上這事∨髑眨” “怎么了预皇?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婉刀。 經(jīng)常有香客問我吟温,道長,這世上最難降的妖魔是什么突颊? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任鲁豪,我火速辦了婚禮,結(jié)果婚禮上律秃,老公的妹妹穿的比我還像新娘爬橡。我一直安慰自己,他們只是感情好棒动,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布糙申。 她就那樣靜靜地躺著,像睡著了一般船惨。 火紅的嫁衣襯著肌膚如雪柜裸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天掷漱,我揣著相機與錄音粘室,去河邊找鬼。 笑死卜范,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鹿榜。 我是一名探鬼主播海雪,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼锦爵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奥裸?” 一聲冷哼從身側(cè)響起险掀,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎湾宙,沒想到半個月后樟氢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡侠鳄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年埠啃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟恶。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡碴开,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出博秫,到底是詐尸還是另有隱情潦牛,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布挡育,位于F島的核電站巴碗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏即寒。R本人自食惡果不足惜橡淆,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒿叠。 院中可真熱鬧明垢,春花似錦、人聲如沸市咽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽施绎。三九已至溯革,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谷醉,已是汗流浹背致稀。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俱尼,地道東北人抖单。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矛绘。 傳聞我的和親對象是個殘疾皇子耍休,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容