文章目錄
- 前言
- 一、SPI是什麼?
- 二、JAVA SPI
- 三、DUBBO SPI:
-
- 1、配置介紹:
- 2、注解作用和規則介紹:
-
-
- 1、@SPI
- 2、@Adaptive
- 3、@Activate
- 4、IOC功能
- 5、@DisableInject
- 6、wrapper包裝類
-
- 3、實戰使用:
- 4、源碼分析
-
- 1、擷取擴充類加載對象 : ExtensionLoader.getExtensionLoader(Protocol.class):
- 2、擷取實作類 extensionLoader.getExtension("hessian")
- 3、擷取自适應類*extensionLoader.getAdaptiveExtension().doExport(url)
- 4、擷取激活類extensionLoader.getActivateExtension(url,"type","group")
- 後言
前言
Dubbo源碼裡面很多基于Dubbo SPI進行擴充類的管理,是以我們先從Dubbo SPI開始講解
一、SPI是什麼?
SPI 全稱為 Service Provider Interface,是一種服務發現機制。SPI 的本質是将接口實作類的全限定名配置在檔案中,并由服務加載器讀取配置檔案,加載實作類。這樣可以在運作時,動态為接口替換實作類;
二、JAVA SPI
預設加載 META-INF/services/接口全限定名 内容為實作類的全限定的類名; 以換行符分隔多個實作類
ServiceLoader<Worker> serviceLoader = ServiceLoader.load(Worker.class);
Iterator<Worker> workerIterator = serviceLoader.iterator();
if(workerIterator.hashNext()){//執行到這就會加載META-INF/services/package.Worker檔案并擷取檔案内容的實作類名稱
Worker robot= workerIterator.next();,//執行個體化傳回
}
缺點:因為不知道實作類是哪個、是以會周遊全部實作類并執行個體化
三、DUBBO SPI:
擴充了java spi的功能, 預設加載 META-INF/services/接口全限定名 || META-INF/dubbo/接口全限定名 || META-INF/dubbo/internal/接口全限定名
實作了服務發現、自适應、自動激活、IOC、AOP功能;
1、配置介紹:
2、注解作用和規則介紹:
1、@SPI
标記在接口表示參與dubbo spi功能,沒有抛出異常;@SPI(value="")可以指定接口預設實作
操作方法:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("hessian") //從配置檔案,擷取指定别名的接口實作類,擷取不到則抛異常
ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension() //從配置檔案,擷取@SPI注解value指定的接口實作類,value=空,則傳回null
擷取過的對象執行個體都會被緩存起來,是以也算一個bean容器
2、@Adaptive
一般标記在接口方法上,并且被标記的方法參數必須要有dubbo的URL類型否則報錯;
@Adaptive(“type”) 表示根據方法參數URL對象中的type來尋找實作類;
如果标記在實作類上表示它就是一個自适應實作類;
操作方法:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension().
doExport(URL.valueOf("http://www.baidu.com?type=hessian"), null);
// 擷取自适應類的實作,并調用其方法doExport,然後擷取type的值hessian,作為實作類;
一般自适應類都是系統運作期生成,代碼如:
package kd.bos.debug.mservice;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements kd.bos.debug.mservice.Protocol {
public void doExport(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.rpc.Invocation arg1) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
if (arg1 == null) throw new IllegalArgumentException("invocation == null");
String methodName = arg1.getMethodName();
String extName = url.getMethodParameter(methodName, "type", "default");//type為@Adaptive的value,為空則預設為接口名如protocol;這個default是@spi("default")擷取,即預設擴充類
//如果@Adaptive的value=protocol則會翻譯成 String extName= url.getProtocol()==null?"defualt":url.getProtocol();
if(extName == null) throw new IllegalStateException("Fail to get extension(kd.bos.debug.mservice.Protocol) name from url(" + url.toString() + ") use keys([type])");
kd.bos.debug.mservice.Protocol extension = (kd.bos.debug.mservice.Protocol)ExtensionLoader.getExtensionLoader(kd.bos.debug.mservice.Protocol.class).getExtension(extName); //根據url中的值,擷取實作類
extension.doExport(arg0, arg1);
}
}
3、@Activate
一般标記在實作類上 @Activate(value={“provider”},group={“v1”}); 表示根據url中存在key=provider,且指定的group也一樣 則是一個激活的實作類;
操作方法:
List<Protocol> activeProtocolImpls=extensionLoader.getActivateExtension(URL.valueOf("http://www.baidu.com?provider=hessian"),"provider","v1");
// 1、擷取存在@Activate注解的實作類,且滿足注解的value在url的key中能對應,且group為v1的實作類
// 2、擷取别名為hession的實作類
4、IOC功能
預設在找到擴充類實作類執行個體化後,會調用其set方法從springIOC、dubbo spi中給其自動注入成員對象值,即IOC操作
預設dubbo spi 工廠 自動注入的成員對象是一個自适應類;是以接口的方法上必須有@Adaptive注解,才會自動注入成功
5、@DisableInject
标記在實作類的setxx方法上,表示不自動注入,即不會調用實作類的setxx方法給其自動注入值;
6、wrapper包裝類
規定interface的實作類,如果有interfaceImpl(Interface interface)這種自持有的構造函數則被認為是包裝類;
spi擷取的時候會傳回這個包裝類,并把真實的接口實作類注入到Wrapper;包裝類就是裝飾器模式,可以在實作類調用前後做操作;相當于AOP
有多個wrapper類,則是wrapper1(instance) 注入到wrapper2(wrapper1)中;傳回wrapper2這樣;即多個wrapper構成責任鍊模式
//包裝類構造方法案例:
public class XxxProtocolWrapper implements Protocol {
Protocol impl;
public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
}
3、實戰使用:
1、配置
2、接口
3、實作類
4、包裝類
5、自動激活類
6、測試IOC自動注入類
7、測試類
public static void main(String[] str){
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
extensionLoader.getExtension("hessian").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null);
extensionLoader.getExtension("dubbo").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null);
System.out.println(extensionLoader.getDefaultExtension());
List<Protocol> activeProtocolImpls=extensionLoader.getActivateExtension(URL.valueOf("http://www.baidu.com?provider=hessian"),"provider","v1");
for(Protocol protocol:activeProtocolImpls){
protocol.doExport(URL.valueOf("http://www.baidu.com?type=hessian"),null);
}
extensionLoader.getExtension("auto").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null);
extensionLoader.getAdaptiveExtension().doExport(URL.valueOf("http://www.baidu.com?type=hessian"), new Invocation() {...});
}
8、運作結果:
do export before
HessianProtocol
do export after
do export before
DubboProtocol
do export after
null
do export before
A activateProtocol
do export after
do export before
HessianProtocol
do export after
do export before
the auto inject bean is [kd.bos.debug.mservice.Protocol$Adpative]
do export after
do export before
HessianProtocol
do export after
4、源碼分析
1、擷取擴充類加載對象 : ExtensionLoader.getExtensionLoader(Protocol.class):
2、擷取實作類 extensionLoader.getExtension(“hessian”)
此時經過loadFile,已經把配置檔案中的 普通實作類、有@Activate注解的實作類、有@Adaptive注解的實作類、滿足wrapper結果的包裝類 都給加載出來了;
現在我們再看上面createExtension方法裡面的 自動注入 injectExtension方法 (高版本的dubbo會在此方法判斷,方法沒有@DisableInject注解才自動注入)
3、擷取自适應類*extensionLoader.getAdaptiveExtension().doExport(url)
4、擷取激活類extensionLoader.getActivateExtension(url,“type”,“group”)
後言
Dubbo SPI的功能講完了,接下來會講解一般項目內建dubbo的入口在哪,原理是什麼,以及dubbo常用參數的配置詳情,zk存儲節點的介紹;