天天看點

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

文章目錄

  • 前言
  • 一、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、配置介紹:

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

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、配置

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

2、接口

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

3、實作類

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

4、包裝類

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

5、自動激活類

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

6、測試IOC自動注入類

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

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):

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

2、擷取實作類 extensionLoader.getExtension(“hessian”)

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

此時經過loadFile,已經把配置檔案中的 普通實作類、有@Activate注解的實作類、有@Adaptive注解的實作類、滿足wrapper結果的包裝類 都給加載出來了;

現在我們再看上面createExtension方法裡面的 自動注入 injectExtension方法 (高版本的dubbo會在此方法判斷,方法沒有@DisableInject注解才自動注入)

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

3、擷取自适應類*extensionLoader.getAdaptiveExtension().doExport(url)

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

4、擷取激活類extensionLoader.getActivateExtension(url,“type”,“group”)

Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言
Dubbo原理<一> Dubbo SPI詳解前言一、SPI是什麼?二、JAVA SPI三、DUBBO SPI:後言

後言

Dubbo SPI的功能講完了,接下來會講解一般項目內建dubbo的入口在哪,原理是什麼,以及dubbo常用參數的配置詳情,zk存儲節點的介紹;