這篇博文是我決心深度學習Dubbo框架時記錄的筆記, 主題是Dubbo的拓展點, 下面的幾個部分相對來講比較零散, 貌似是不和主題掛鉤的 , 而且是一些很冷門的知識點 , 可是它們確實是深刻學習Dubbo的前置知識java
細化一下上圖的各個組成部分:apache
Dubbo本身有個封裝類叫URL以下: URL: 全稱 Uniform Resources Loactor 統一資源定位符, 它是不可變的, 也是線程安全的編程
其實, Dubbo它做爲一款RPC通訊框架, 主體功能就是負責在服務集羣中各個點之間進行數據的傳遞, 打個例子好比: 服務消費者調用服務的提供者,這個過程當中的通訊是Dubbo框架實現的, 通訊的格式就比如自定義協議同樣, Dubbo將服務提供者和服務消費者兩種之間進行數據傳遞 須要的協議信息/ 端口號信息/ 請求那個接口 / 參數信息 / 帳號 / 密碼信息. 等一系列的信息進行封裝,因而上圖中的 URL 誕生了api
對URL最直觀的理解: URL是dobbo 對一系列數據的封裝, 方便代碼的編寫, 參數的傳遞緩存
不少人也將URL稱爲Dubbo的消息總線, 說URL貫穿於Dubbo的上下文, 我感受到這個結論也許是這樣得出的, 就是說 Dubbo做爲一款RPC框架, 首要的任務就是 RPC 遠程過程調用, 怎麼樣找到提供服務的機器呢? 不管是發起socket 仍是藉助Thrift或者Netty這種框架實現也罷, 前提是得知道提供服務的機器在哪裏, 它的哪些接口對外暴露服務 , 沒錯! 這些信息都被Dubbo封裝在了URL中安全
# 描述 Dubbo 協議的服務 Dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 # 描述 zookeeper 註冊中心 zookeeper://127.0.0.1:2181/org.apache.Dubbo.registry.RegistryService?application=demo-consumer&Dubbo=2.0.2&interface=org.apache.Dubbo.registry.RegistryService&pid=1214&qos.port=33333×tamp=1545721981946 # 描述消費者 服務 consumer://30.5.120.217/org.apache.Dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&Dubbo=2.0.2&interface=org.apache.Dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer×tamp=1545721827784 # for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null 192.168.1.3:20880 # for this case, url protocol = file, url host = null, url path = home/user1/router.js file:///home/user1/router.js?type=script ... 更多參照URL源碼
invoker 直譯調用者網絡
指代程序中的調用對象, 包含了 接口名 / 方法名 / 參數類型列表 / 參數值列表 等架構
怎麼理解SPI機制呢?app
若是說SPI是java提供的一種拓展機制, 實際上是不明確的, 結合java自己的語言特性來講, SPI直觀的看就是 基於接口的編程 + 策略模式 + 配置文件 組合實現的動態加載機制, 用大白話解釋就是說, 一個框架的設計爲了後期的拓展性, 確定先會在頂層設計接口, 而後再爲這些接口提供一些默認的實現類, 未了良好的拓展性, 若是想讓, 若是想實現容許當前框架 識別 / 加載 / 使用 第三方提供的jar包時 , 就可使用SPI實現接口的動態加載, 只要遵循SPI的規範, java就能將咱們本身的類也加載進JVM供咱們使用負載均衡
提及來總歸是模糊的, 看下面的小Demo天然就懂了
// 接口 public interface Person { String getName(); } // 實現類一: public class Student implements Person { @Override public String getName() { return "Student"; } } // 實現類二: public class Teacher implements Person { @Override public String getName() { return "Teacher"; } }
在resource/META-INF/services/ 目錄下面添加配置文件, 文件名稱爲 Person接口的全限定名, 內容以下
com.changwu.javaspi.api.Student com.changwu.javaspi.api.Teacher
測試程序:
public class Test { public static void main(String[] args) { // 加載接口中的實現類 ServiceLoader<Person> load = ServiceLoader.load(Person.class); Iterator<Person> iterator = load.iterator(); while (iterator.hasNext()){ Person next = iterator.next(); System.out.println(next.getName()); } } }
測試結果控制檯輸出以下:
Student Teacher
Dubbo本身也封裝了一套SPI機制, 並將此做爲它的擴展點,若是咱們有更好的想法, 可使用Dubbo這個特性加將咱們本身的類注入給Dubbo, 它用法和JDK原生的SPI類似, 不一樣點在哪裏呢? Dubbo的更強大, 好比相對於JDK的SPI , 它支持根據名稱獲取出指定的拓展類
一個小demo
@SPI public interface PersonInterface { String getName(); }
public class Student implements PersonInterface { @Override public String getName() { return "Student"; } } public class Teacher implements PersonInterface { @Override public String getName() { return "Teacher"; } }
public class Test { public static void main(String[] args) { // todo 第一點: Dubbo 的SPI算做是他的一個可擴展的機制 ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); PersonInterface carInterface = extensionLoader.getExtension("student"); System.out.println(carInterface.getName()); } }
Spring 的IOC確定是鼎鼎大名的, 很直接的能想到Spring的 @Autowired 註解, 或者的配置文件版本的 <bean>
標籤中能夠幫咱們自動維護bean之間的相互的依賴的關係
Dubbo 也實現了本身的IOC
好比下面的代碼這樣: Human.java 中依賴了 PersonInterface 類型的對象, 打眼看上去, 這個對象確定是藉助咱們提供的setter方法完成的注入
public class Human implements PersonInterface { private PersonInterface carInterface; //todo 第一個關注點: 咱們的關注點就是說, BeazCar 會幫咱們將哪個實現類當成入參注入進來呢? //todo 答案是 URL ,Dubbo本身封裝的URL, 統一資源定位符, Dubbo 會解析入參位置的 url中封裝的map //todo map中的key 與 CarInteface中的使用 @Adaptive("car") 註解標記的value對應, 那麼值就是將要注入的實際類型 //todo 第二個關注點: Dubbo底層極可能是經過反射使用構造方法完成的屬性注入 public void setCarInterface(PersonInterface carInterface) { this.carInterface = carInterface; } @Override public String getColor(URL url) { System.out.println("i am Human "); return "i am Human + " + carInterface.getColor(url); } }
那麼問題來了, 假如咱們在配置文件中添加了多個PersonInterface
接口的實現類, 那Dubbo是如何得知須要注入哪個的呢? 答案就在入參位置的URL中, 也就是我在 知識儲備二中提到的概念URL
能夠看下面這段測試代碼, 怎麼讀下面的這段代碼呢?
單獨看 (PersonInterface) extensionLoader.getExtension("human");
其實就是前面所說的 Dubbo的SPI機制, 可是在這個基礎上多出來的邏輯是啥呢? 是咱們構建了一個URL, 那爲何加進去一個URL? 由於上面的示例代碼說了, human依賴了一個 PersonInterface 類型的變量, Dubbo就是根據這個URL變量, 進而得知本身到底該該注入哪個變量Personinterface實例的 (由於我提供了兩個 一個是Student , 另外一個是Teacher)
此外, 他須要的是map , 咱們給它的也是一個hashmap , 特性就是HashMap的key是不重複的, 用大白話說, 它的底層確定是 key=value 惟一綁定, 而且key也不會出現重複的狀況
public class Test { public static void main(String[] args) { // todo 源碼的入口, 進入 getExtensionLoader() ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); HashMap<String, String> map = new HashMap<>(); map.put("human", "student"); URL url = new URL("", "",1,map); // todo 繼續跟進這個方法 PersonInterface carInterface = (PersonInterface) extensionLoader.getExtension("human"); System.out.println(carInterface.getName(url)); } }
那說了這麼多, 到底注入的是哪個對象呢? 從map.put("human", "student");
也能很清楚的看出來, 不就是Student嗎? 是的, 確實是它, 可是還少了點東西, 就是Personinterface怎麼編寫呢? 以下:
// @SPI("stu") 能夠給註解添加參數, 參數表示 CarInterface 的默認實現類 @SPI public interface PersonInterface { // todo 下面的註解很重要, 啥意思呢? 能夠點進這個註解, 我有一些翻譯 // 驗證AOP, 依然注入的信息從 url中獲取出來 @Adaptive("human") String getName(URL url); }
看上面的代碼, 除了@SPI註解, 還有一個註解就是@Adaptive註解, 這個註解的value部分決定了Dubbo到底須要注入哪個 ExtensionObject
由於Dubbo在啓動的過程當中會去讀取/META-INF/services/ Dubbo-SPI配置文件, 並將每行數據讀取維護在一個map中, key就是咱們自定義的名字, 值就是左邊的全類名
看下面咱們傳遞進去的是 human , 表示告訴Dubbo, 讓Dubbo拿着human去查找, 很顯然Dubbo把咱們前面傳遞給它的student
找出來, 有了Student 進一步再從上下文中全部的 ExtensionObject中(包含了咱們在配置文件中添加進去的Personinterface的兩個實現) 找到具體的注入對象
仍是說, AOP是面向切面編程的思想, Spring本身實現了一套, Dubbo 也實現了一套
驗證Dubbo的AOP實現類以下:
public class PersonWrapper implements PersonInterface { // todo 驗證Dubbo的自動注入 private PersonInterface carInterface; // todo 根據構造方法進行注入 public PersonWrapper(PersonInterface in){ // 假設傳遞進來的就是具體的實現類 this.carInterface=in; } // todo 當咱們將 CarWrapper 配置進 Dubbo的 spi中時, 經過Dubbo的Spi獲取CarInterface執行時,下面的方法就會被執行 @Override public String getName() { System.out.println("before... "); String color = carInterface.getName(); System.out.println("after... "); return "getName"; } }
public static void main(String[] args) { // todo 第一點: Dubbo 的SPI算做是他的一個可擴展的機制 ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); PersonInterface carInterface = extensionLoader.getExtension("student"); System.out.println(carInterface.getName()); }
結果以下:
before... after... getName
如過存在多個AOP加強類, 好比從上到下出現的順序是 w1 w2 ... 那麼加強的邏輯添加順序是 before2 before1
下一篇博文就是探究Dubbo的這些拓展點的底層實現細節了 , 仍是挺帶勁的...
最後打一個小廣告: 我是bloger 賜我白日夢, 本科大三在讀, 熱衷java研發, 指望有一份Java相關實習崗位的工做, 能夠全職實習半年左右, 最理想城市是北京, 求大佬的內推哇