日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Dubbo如何通過SPI提高框架的可擴展性?

介紹

10年的安州網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。營銷型網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整安州建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)公司從事“安州網(wǎng)站設(shè)計”,“安州網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。

最近看了一下Dubbo的源碼,國人寫的框架和國外的果然是兩種不同的風(fēng)格,Dubbo的源碼還是比較清晰容易懂的。Spring框架一個Bean的初始化過程就能繞死在源碼中.

Dubbo的架構(gòu)是基于分層來設(shè)計的,每層執(zhí)行固定的功能,上層依賴下層,下層的改變對上層不可見,每層都是可以被替換的組件

 

Service和Config為API接口層,讓Dubbo使用者方便的發(fā)布和引用服務(wù)

其他各層均為SPI層,意味著每層都是組件化的,可以被替換

例如,注冊中心可以用Redis,Zookeeper。傳輸協(xié)議可以用dubbo,rmi,hessian等。

網(wǎng)絡(luò)通信可以用mina,netty。序列化可以用fastjson,hessian2,java原生的方式等

SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機制。SPI 的本質(zhì)是將接口實現(xiàn)類的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實現(xiàn)類。這樣可以在運行時,動態(tài)為接口替換實現(xiàn)類。正因此特性,我們可以很容易的通過 SPI 機制為我們的程序提供拓展功能

那么Dubbo的SPI是怎么實現(xiàn)的呢?先來了解一下Java SPI

Java SPI

Java SPI是通過策略模式實現(xiàn)的,一個接口提供多個實現(xiàn)類,而使用哪個實現(xiàn)類不在程序中確定,而是配置文件配置的,具體步驟如下

  • 定義接口及其對應(yīng)的實現(xiàn)類
  • 在META-INF/services目錄下創(chuàng)建以接口全路徑命名的文件
  • 文件內(nèi)容為實現(xiàn)類的全路徑名
  • 在代碼中通過java.util.ServiceLoader#load加載具體的實現(xiàn)類

寫個Demo演示一下

 
 
 
 
  1. public interface Car { 
  2.  
  3.     void getBrand(); 
 
 
 
 
  1. public class BenzCar implements Car { 
  2.  
  3.     @Override 
  4.     public void getBrand() { 
  5.         System.out.println("benz"); 
  6.     } 
 
 
 
 
  1. public class BMWCar implements Car { 
  2.  
  3.     @Override 
  4.     public void getBrand() { 
  5.         System.out.println("bmw"); 
  6.     } 

org.apache.dubbo.Car的內(nèi)容如下

 
 
 
 
  1. org.apache.dubbo.BenzCar 
  2. org.apache.dubbo.BMWCar 

測試類

 
 
 
 
  1. public class JavaSpiDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ServiceLoader carServiceLoader = ServiceLoader.load(Car.class); 
  5.         // benz 
  6.         // bmw 
  7.         carServiceLoader.forEach(Car::getBrand); 
  8.     } 

Dubbo SPI

 

用Dubbo SPI將上面的例子改造一下

 
 
 
 
  1. @SPI 
  2. public interface Car { 
  3.  
  4.     void getBrand(); 
 
 
 
 
  1. public class BenzCar implements Car { 
  2.  
  3.     @Override 
  4.     public void getBrand() { 
  5.         System.out.println("benz"); 
  6.     } 
 
 
 
 
  1. public class BMWCar implements Car { 
  2.     @Override 
  3.     public void getBrand() { 
  4.         System.out.println("bmw"); 
  5.     } 

org.apache.dubbo.quickstart.Car的內(nèi)容如下

 
 
 
 
  1. benz=org.apache.dubbo.quickstart.BenzCar 
  2. bmw=org.apache.dubbo.quickstart.BMWCar 

測試類

 
 
 
 
  1. public class DubboSpiDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Car.class); 
  5.         Car car = extensionLoader.getExtension("benz"); 
  6.         car.getBrand(); 
  7.     } 
 
 
 
 
  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target({ElementType.TYPE}) 
  4. public @interface SPI { 
  5.  
  6.     String value() default ""; 
  7.  

@SPI標(biāo)記接口是一個Dubbo SPI接口,即是一個擴展點,value屬性可以指定默認實現(xiàn)

Dubbo 并未使用 Java 原生的 SPI 機制,而是對其進行了增強,使其能夠更好的滿足需求。Dubbo SPI的優(yōu)點如下

  • JDK標(biāo)準(zhǔn)的SPI會一次性實例化擴展點的所有實現(xiàn)。而Dubbo SPI能實現(xiàn)按需加載
  • Dubbo SPI增加了對擴展點Ioc和Aop的支持

Dubbo SPI的實現(xiàn)步驟如下

  1. 定義接口及其對應(yīng)的實現(xiàn)類,接口上加@SPI注解,表明這是一個擴展類
  2. 在META-INF/services目錄下創(chuàng)建以接口全路徑命名的文件
  3. 文件內(nèi)容為實現(xiàn)類的全路徑名
  4. 在代碼中通過ExtensionLoader加載具體的實現(xiàn)類

Dubbo SPI 擴展點的特性自動包裝

擴展類的構(gòu)造函數(shù)是一個擴展點,則認為這個類是一個Wrapper類,即AOP

用例子演示一下

 
 
 
 
  1. @SPI 
  2. public interface Car { 
  3.  
  4.     void getBrand(); 
 
 
 
 
  1. public class BenzCar implements Car { 
  2.     @Override 
  3.     public void getBrand() { 
  4.         System.out.println("benz"); 
  5.     } 
 
 
 
 
  1. public class CarWrapper implements Car { 
  2.  
  3.     private Car car; 
  4.  
  5.     public CarWrapper(Car car) { 
  6.         this.car = car; 
  7.     } 
  8.  
  9.     @Override 
  10.     public void getBrand() { 
  11.         System.out.println("start"); 
  12.         car.getBrand(); 
  13.         System.out.println("end"); 
  14.     } 

org.apache.dubbo.aop.Car內(nèi)容如下(resources\META-INF\services目錄下)

 
 
 
 
  1. benz=org.apache.dubbo.aop.BenzCar 
  2. org.apache.dubbo.aop.CarWrapper 

測試類

 
 
 
 
  1. public class DubboSpiAopDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Car.class); 
  5.         Car car = extensionLoader.getExtension("benz"); 
  6.         // start 
  7.         // benz 
  8.         // end 
  9.         car.getBrand(); 
  10.     } 

BenzCar是一個擴展類,CarWrapper是一個包裝類,當(dāng)獲取BenzCar的時候?qū)嶋H獲取的是被CarWrapper包裝后的對象,類似代理模式

自動加載

如果一個擴展類是另一個擴展類的成員變量,并且擁有set方法,框架會自動注入這個擴展點的實例,即IOC。先定義2個擴展點

org.apache.dubbo.ioc.Car(resources\META-INF\services目錄下)

 
 
 
 
  1. benz=org.apache.dubbo.ioc.BenzCar 

org.apache.dubbo.ioc.Wheel(resources\META-INF\services目錄下)

 
 
 
 
  1. benz=org.apache.dubbo.ioc.BenzWheel 
  2. @SPI 
  3. public interface Wheel { 
  4.  
  5.     void getBrandByUrl(); 
 
 
 
 
  1. public class BenzWheel implements Wheel { 
  2.  
  3.     @Override 
  4.     public void getBrandByUrl() { 
  5.         System.out.println("benzWheel"); 
  6.     } 
 
 
 
 
  1. @SPI 
  2. public interface Car { 
  3.  
  4.     void getBrandByUrl(); 
 
 
 
 
  1. public class BenzCar implements Car { 
  2.  
  3.     private Wheel wheel; 
  4.  
  5.     public void setWheel(Wheel wheel) { 
  6.         this.wheel = wheel; 
  7.     } 
  8.  
  9.     @Override 
  10.     public void getBrandByUrl() { 
  11.         System.out.println("benzCar"); 
  12.         wheel.getBrandByUrl(); 
  13.     } 

測試demo

 
 
 
 
  1. public class DubboSpiIocDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Car.class); 
  5.         Car car = extensionLoader.getExtension("benz"); 
  6.         car.getBrandByUrl(); 
  7.     } 

我跑這個代碼的時候直接報異常,看了一下官網(wǎng)才發(fā)現(xiàn)dubbo是可以注入接口的實現(xiàn)的,但不像spring那么智能,

dubbo必須用URL(類似總線)來指定擴展類對應(yīng)的實現(xiàn)類.。這就不得不提到@Adaptive注解了

自適應(yīng)

使用@Adaptive注解,動態(tài)的通過URL中的參數(shù)來確定要使用哪個具體的實現(xiàn)類

 
 
 
 
  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target({ElementType.TYPE, ElementType.METHOD}) 
  4. public @interface Adaptive { 
  5.  
  6.     String[] value() default {}; 
  7.  
 
 
 
 
  1. @SPI 
  2. public interface Wheel { 
  3.  
  4.     @Adaptive("wheel") 
  5.     void getBrandByUrl(URL url); 
 
 
 
 
  1. public class BenzWheel implements Wheel { 
  2.  
  3.     @Override 
  4.     public void getBrandByUrl(URL url) { 
  5.         System.out.println("benzWheel"); 
  6.     } 
 
 
 
 
  1. @SPI 
  2. public interface Car { 
  3.  
  4.     void getBrandByUrl(URL url); 
 
 
 
 
  1. public class BenzCar implements Car { 
  2.  
  3.     // 這個里面存的是代理對象 
  4.     private Wheel wheel; 
  5.  
  6.     public void setWheel(Wheel wheel) { 
  7.         this.wheel = wheel; 
  8.     } 
  9.  
  10.     @Override 
  11.     public void getBrandByUrl(URL url) { 
  12.         System.out.println("benzCar"); 
  13.         // 代理類根據(jù)URL找到實現(xiàn)類,然后再調(diào)用實現(xiàn)類 
  14.         wheel.getBrandByUrl(url); 
  15.     } 
 
 
 
 
  1. public class DubboSpiIocDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Car.class); 
  5.         Car car = extensionLoader.getExtension("benz"); 
  6.         Map map = new HashMap<>(); 
  7.         // 指定wheel的實現(xiàn)類為benz 
  8.         map.put("wheel", "benz"); 
  9.         URL url = new URL("", "", 1, map); 
  10.         // benzCar 
  11.         // benzWheel 
  12.         car.getBrandByUrl(url); 
  13.     } 

可以看到BenzCar對象成功注入了BenzWheel。BenzCar中其實注入的是BenzWheel的代碼對象,這個代理對象會根據(jù)@Adaptive("wheel")獲取到wheel,然后從url中找到key為wheel的值,這個值即為實現(xiàn)類對應(yīng)的key。

上面的注釋提到BenzCar里面注入的Wheel其實是一個代理對象(框架幫我們生成),在代理對象中根據(jù)url找到相應(yīng)的實現(xiàn)類,然后調(diào)用實現(xiàn)類。

因為代理對象是框架在運行過程中幫我們生成的,沒有文件可以查看,所以用Arthas來查看一下生成的代理類

 
 
 
 
  1. curl -O https://alibaba.github.io/arthas/arthas-boot.jar 
  2. java -jar arthas-boot.jar 
  3. # 根據(jù)前面的序號選擇進入的進程,然后執(zhí)行下面的命令 
  4. jad org.apache.dubbo.adaptive.Wheel$Adaptive 

生成的Wheel

 
 
 
 
  1. package org.apache.dubbo.adaptive; 
  2.  
  3. import org.apache.dubbo.adaptive.Wheel; 
  4. import org.apache.dubbo.common.URL; 
  5. import org.apache.dubbo.common.extension.ExtensionLoader; 
  6.  
  7. public class Wheel$Adaptive 
  8. implements Wheel { 
  9.     public void getBrandByUrl(URL uRL) { 
  10.         if (uRL == null) { 
  11.             throw new IllegalArgumentException("url == null"); 
  12.         } 
  13.         URL uRL2 = uRL; 
  14.         String string = uRL2.getParameter("wheel"); 
  15.         if (string == null) { 
  16.             throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.adaptive.Wheel) name from url (").append(uRL2.toString()).append(") use keys([wheel])").toString()); 
  17.         } 
  18.         Wheel wheel = (Wheel)ExtensionLoader.getExtensionLoader(Wheel.class).getExtension(string); 
  19.         wheel.getBrandByUrl(uRL); 
  20.     } 

@Adaptive可以標(biāo)記在類上或者方法上

標(biāo)記在類上:將該實現(xiàn)類直接作為默認實現(xiàn),不再自動生成代碼

標(biāo)記在方法上:通過參數(shù)動態(tài)獲得實現(xiàn)類,比如上面的例子

用源碼演示一下用在類上的@Adaptiv,Dubbo為自適應(yīng)擴展點生成代碼,如我們上面的WheelAdaptive類如下所示¨G30G??@Adaptive可以標(biāo)記在類上或者方法上??標(biāo)記在類上:將該實現(xiàn)類直接作為默認實現(xiàn),不再自動生成代碼標(biāo)記在方法上:通過參數(shù)動態(tài)獲得實現(xiàn)類,比如上面的例子用源碼演示一下用在類上的@Adaptiv,Dubbo為自適應(yīng)擴展點生成代碼,如我們上面的WheelAdaptive,但生成的代碼還需要編譯才能生成class文件。我們可以用JavassistCompiler(默認的)或者JdkCompiler來編譯(需要配置),這個小小的功能就用到了@Adaptive

如果想用JdkCompiler需要做如下配置

 
 
 
 
  1.  

Compiler類圖如下

 
 
 
 
  1. @SPI("javassist") 
  2. public interface Compiler { 
  3.  
  4.     Class compile(String code, ClassLoader classLoader); 
  5.  

Compiler用@SPI指定了默認實現(xiàn)類為javassist

源碼中獲取Compiler調(diào)用了如下方法

 
 
 
 
  1. org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); 

getAdaptiveExtension()會獲取自適應(yīng)擴展類,那么這個自適應(yīng)擴展類是誰呢?

是AdaptiveCompiler,因為類上有@Adaptive注解

 
 
 
 
  1. @Adaptive 
  2. public class AdaptiveCompiler implements Compiler { 
  3.  
  4.     private static volatile String DEFAULT_COMPILER; 
  5.  
  6.     public static void setDefaultCompiler(String compiler) { 
  7.         DEFAULT_COMPILER = compiler; 
  8.     } 
  9.  
  10.     /** 
  11.      * 獲取對應(yīng)的Compiler,并調(diào)用compile做編譯 
  12.      * 用戶設(shè)置了compiler,就用設(shè)置了的,不然就用默認的 
  13.      */ 
  14.     @Override 
  15.     public Class compile(String code, ClassLoader classLoader) { 
  16.         Compiler compiler; 
  17.         ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Compiler.class); 
  18.         String name = DEFAULT_COMPILER; // copy reference 
  19.         if (name != null && name.length() > 0) { 
  20.             // 用用戶設(shè)置的 
  21.             compiler = loader.getExtension(name); 
  22.         } else { 
  23.             // 用默認的 
  24.             compiler = loader.getDefaultExtension(); 
  25.         } 
  26.         return compiler.compile(code, classLoader); 
  27.     } 
  28.  

從compile方法可以看到,如果用戶設(shè)置了編譯方式,則用用戶設(shè)置的,如果沒有設(shè)置則用默認的,即JavassistCompiler

自動激活

使用@Activate注解,可以標(biāo)記對應(yīng)的擴展點默認被激活使用

 
 
 
 
  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target({ElementType.TYPE, ElementType.METHOD}) 
  4. public @interface Activate { 
  5.  
  6.     // 所屬組,例如消費端,服務(wù)端 
  7.     String[] group() default {}; 
  8.  
  9.     // URL中包含屬性名為value的鍵值對,過濾器才處于激活狀態(tài) 
  10.     String[] value() default {}; 
  11.  
  12.     // 指定執(zhí)行順序,before指定的過濾器在該過濾器之前執(zhí)行 
  13.     @Deprecated 
  14.     String[] before() default {}; 
  15.  
  16.     // 指定執(zhí)行順序,after指定的過濾器在該過濾器之后執(zhí)行 
  17.     @Deprecated 
  18.     String[] after() default {}; 
  19.  
  20.     // 指定執(zhí)行順序,值越小,越先執(zhí)行 
  21.     int order() default 0; 

可以通過指定group或者value,在不同條件下獲取自動激活的擴展點。before,after,order是用來排序的,感覺一個order參數(shù)就可以搞定排序的功能,所以官方把before,after標(biāo)記為@Deprecated

Dubbo Filter就是基于這個來實現(xiàn)的。Dubbo Filter是Dubbo可擴展性的一個體現(xiàn),可以在調(diào)用過程中對請求進行進行增強

我寫個demo演示一下這個自動激活是怎么工作的

 
 
 
 
  1. @SPI 
  2. public interface MyFilter {  
  3.     void filter(); 

consumer組能激活這個filter

 
 
 
 
  1. @Activate(group = {"consumer"}) 
  2. public class MyConsumerFilter implements MyFilter { 
  3.     @Override 
  4.     public void filter() { 
  5.  
  6.     } 

provider組能激活這個filter

 
 
 
 
  1. @Activate(group = {"provider"}) 
  2. public class MyProviderFilter implements MyFilter { 
  3.     @Override 
  4.     public void filter() { 
  5.  
  6.     } 

consumer組和provide組都能激活這個filter

 
 
 
 
  1. @Activate(group = {"consumer", "provider"}) 
  2. public class MyLogFilter implements MyFilter { 
  3.     @Override 
  4.     public void filter() { 
  5.  
  6.     } 

consumer組和provide組都能激活這個filter,同時url中指定key的value為cache

 
 
 
 
  1. @Activate(group = {"consumer", "provider"}, value = "cache") 
  2. public class MyCacheFilter implements MyFilter { 
  3.     @Override 
  4.     public void filter() { 
  5.  
  6.     } 

測試類如下

getActivateExtension有3個參數(shù),依次為url, key, group

 
 
 
 
  1. public class ActivateDemo { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(MyFilter.class); 
  5.         // url中沒有參數(shù) 
  6.         URL url = URL.valueOf("test://localhost"); 
  7.         List allFilterList = extensionLoader.getActivateExtension(url, "", null); 
  8.         /** 
  9.          * org.apache.dubbo.activate.MyConsumerFilter@53e25b76 
  10.          * org.apache.dubbo.activate.MyProviderFilter@73a8dfcc 
  11.          * org.apache.dubbo.activate.MyLogFilter@ea30797 
  12.          * 
  13.          * 不指定組則所有的Filter都被激活 
  14.          */ 
  15.         allFilterList.forEach(item -> System.out.println(item)); 
  16.         System.out.println(); 
  17.  
  18.         List consumerFilterList = extensionLoader.getActivateExtension(url, "", "consumer"); 
  19.         /** 
  20.          * org.apache.dubbo.activate.MyConsumerFilter@53e25b76 
  21.          * org.apache.dubbo.activate.MyLogFilter@ea30797 
  22.          * 
  23.          * 指定consumer組,則只有consumer組的Filter被激活 
  24.          */ 
  25.         consumerFilterList.forEach(item -> System.out.println(item)); 
  26.         System.out.println(); 
  27.  
  28.         // url中有參數(shù)myfilter 
  29.         url = URL.valueOf("test://localhost?myfilter=cache"); 
  30.         List customerFilter = extensionLoader.getActivateExtension(url, "myfilter", "consumer"); 
  31.         /** 
  32.          * org.apache.dubbo.activate.MyConsumerFilter@53e25b76 
  33.          * org.apache.dubbo.activate.MyLogFilter@ea30797 
  34.          * org.apache.dubbo.activate.MyCacheFilter@aec6354 
  35.          * 
  36.          * 指定key在consumer組的基礎(chǔ)上,MyCacheFilter被激活 
  37.          */ 
  38.         customerFilter.forEach(item -> System.out.println(item)); 
  39.         System.out.println(); 
  40.     } 

總結(jié)一下就是,getActivateExtension不指定組就是激活所有的Filter,指定組則激活指定組的Filter。指定key則從Url中根據(jù)key取到對應(yīng)的value,假設(shè)為cache,然后把@Activate注解中value=cache的Filter激活

即group用來篩選,value用來追加,Dubbo Filter就是靠這個屬性激活不同的Filter的

ExtensionLoader的工作原理

ExtensionLoader是整個Dubbo SPI的主要實現(xiàn)類,有如下三個重要方法,搞懂這3個方法基本上就搞懂Dubbo SPI了。

加載擴展類的三種方法如下

  1. getExtension(),獲取普通擴展類
  2. getAdaptiveExtension(),獲取自適應(yīng)擴展類
  3. getActivateExtension(),獲取自動激活的擴展類

getExtension()上面的例子中已經(jīng)有了。自適應(yīng)的特性上面已經(jīng)演示過了,當(dāng)獲取Wheel的實現(xiàn)類是框架會調(diào)用getAdaptiveExtension()方法。

代碼就不放了,這3個方法的執(zhí)行過程還是比較簡單的,如果你有看不懂的,可以看我給源碼加的注釋。

https://github.com/erlieStar/dubbo-analysis

理解了Dubbo SPI你應(yīng)該就把Dubbo搞懂一半了,剩下就是一些服務(wù)導(dǎo)出,服務(wù)引入,服務(wù)調(diào)用的過程了

本文轉(zhuǎn)載自微信公眾號「Java識堂」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java識堂公眾號。


本文名稱:Dubbo如何通過SPI提高框架的可擴展性?
網(wǎng)址分享:http://www.5511xx.com/article/djpsijp.html