SPI 機制是實現(xiàn)可擴展性的一種方式尺锚。上一篇介紹了
JDK SPI
的使用姿勢和基本原理英上,本節(jié)來分析Dubbo SPI
的基本使用、適配類使用眉撵、AOP 使用帐要、IOC 使用以及激活點的使用(基于 dubbo 2.6.6)把敞。
可參考的 SPI 實現(xiàn):
一、Dubbo SPI 基本使用
- Dubbo 配置文件名依然是:
SPI 接口的全接口名
- Dubbo SPI 會從以下三個目錄讀取配置文件:
- META-INF/dubbo/internal/:該目錄用于存儲 Dubbo 框架本身提供的 SPI 擴展實現(xiàn)榨惠,eg.
- 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 默認只提供了兩個手動編寫的適配類
AdaptiveExtensionFactory
和AdaptiveCompiler
)- 根據(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()
來看):
- 首先獲取除了傳入的 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ù)名)
- 之后獲取傳入的 spiKey 集合(values)指定的 SPI 激活點實現(xiàn)類(稱為 usr 激活點)
- 傳入的spiKey 集合(values)不包含(-name工秩,name 表示當(dāng)前處理到的 SPI 激活點的 spiKey):也就是說配置 -name 可以排除掉某個實現(xiàn)類;
- 將 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);
}
}