承接上篇, 本篇博文的主題就是認認真真捋一捋, 看一下 Dubbo是如何實現他的IOC / AOP / 以及Dubbo SPI這個拓展點的java
本篇的話整體上分紅兩部分進行展開程序員
以下代碼是追蹤的起點:apache
我也是看了好多遍才勉強將這個過程整理明白一些, 可是根據以往的經驗來講, 過一倆月以後我可能就會淡忘這個流程... 爲了讓本身一段時間後快速的回憶起來這個流程, 因此我要對本身說下面一段話編程
Dubbo的拓展點編碼實現中, 會反反覆覆的出現下面這段代碼api
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(XXX.class); extensionLoader.getExtension("XXX");
緩存先說這段代碼在幹什麼? 其實上它就是在爲 Dubbo原生的SPI接口, 或者是用戶提供的SPI接口 結合SPI的配置文件中的配置, 找到這些SPI接口的實現類, 而且這個過程當中穿插Dubbo的IOC已經AOP機制安全
不得不服氣, 這一段代碼的實現, 由於這段代碼設計不只僅能加載Dubbo提供的原生的SPI接口, 也能加載使用 用戶自定義的SPI , 詳細的過程在下文中展開, 妙!!! app
應該得, 隆重的介紹一下這個明星類 ExtensionLoader.javaide
從名字上看, ExtensionLoader , 見名知意, 拓展點的加載器, 那什麼是Dubbo的拓展點呢? 拓展點就是Dubbo容許用戶參與到Dubbo環境的初始化這個過程當中來, 容許用戶定製Dubbo行爲, 諸如 Dubbo的 SPI / IOC / AOP (上一篇博文主要的學習內容就是Dubbo的拓展點的使用)函數
見名知意: ExtensionLoader 拓展點的加載器, 就是使用這個封裝類, 咱們能夠加載Dubbo提供的拓展點, 說白了, 其實加載爲SPI接口找到實現類, 以及完成這些實現類之間的 AOP加強 + IOC 依賴注入的過程
此外這個類頗有必要看, 爲啥呢? 第一點就是說它的設計很巧妙, 代碼的抽象和複用能力都很好, 第二點就是說, 咱們能夠一睹大神們的風采, 若是 實現本身的SPI , 如何實現本身的IOC AOP
下面就是入口程序中的第一個方法, getExtensionLoader(Class<T> type)
很簡單, 就是根據類型找到對應的 ExtensionLoader, 待會Dubbo就會爲我添加進去SPI接口生成這樣的 ExtensionLoader : org.apache.dubbo.common.extension.ExtensionLoader[com.changwu.ioc.api.PersonInterface]
固然Dubbo也有本身原生的ExtensionLoader
從個人入口程序來看, 很顯然, 我傳遞進來的 type = PersonInterface
, 方法執行的邏輯以下
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } //todo 這裏體現了緩存機制, EXTENSION_LOADERS 其實就是 CurrentHashMap //todo EXTENSION_LOADERS 是 CurrentHashMap , 每一種interfaceType 都對應一個 ExtensionLoader , 可是這些 ExtensionLoader所有被維護在 這個EXTENSION_LOADER中 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
那咱們是第一次進來, 確定是沒有的, 所以咱們看他是如何進行new ExtensionLoader<T>(type)
的, 因此跟進看一下它的構造方法
private ExtensionLoader(Class<?> type) { this.type = type; // 對於一個接口,好比PersonInterface接口,有兩種實現類,一種就是咱們自定義的實現類,好比Student,還有一種就是代理類,對於代理類,能夠由咱們本身實現,也可讓Dubbo幫咱們實現,而代理類主要就是依賴注入時使用 // todo ExtensionFactory 是dubbo的拓展機制工廠, 它裏面封裝了 Dubbo的SPI拓展機制和Spring的拓展機制 // todo ExtensionLoader.getExtensionLoader(ExtensionFactory.class) ===> 獲取 自適應的extension // || || || || || || // todo ExtensionLoader.getExtensionLoader(PersonInterface.class) ===> 昌武, 你獲取的是: extension("human") // todo 你看這是否是挺清晰的 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
在上面的構造方法中, 就有蹊蹺了, 邏輯以下
我所說的有蹊蹺的地方: 咱們原本不是前來建立PersionInterface 的ExtensionLoader嗎? 怎麼先建立 ExtensionFactory的 ExtensionLoader呢?
(由於在建立ExtensionFactory的 ExtensionLoader的過程當中會去加載Dubbo提供的其餘的諸如SpiExtensionFactory這一類的實現, 這些默認的實現的做用就是輔助Dubbo再去解析用戶提供的SPI實現體系)
下面看看這個 ExtensionFactory.class類
沒錯! 它被添加上了@SPI的註解, 說明和 咱們的PersonInterface同樣, 是DubboSPI
那好吧, 既然Dubbo想先完成它的實例化, 就往下看, 我在博文開頭就不停的說, Dubbo設計的很好, 這裏不就遞歸調用getExtensionLoader(type= ExtensionFactory.class)了嗎? 不出意外的話, 再一次的 進去構造方法, 而後在這個三元判斷表達式中發現了 type == ExtensionFactory.class ? null : ExtensionLoader.getE... 前半部分是知足條件的, 而後設置objectFactory=null, 完成 ExtensionFactory的構造, 而後執行getAdaptiveExtension()
這個 getAdaptiveExtension()
一樣須要好好的看看, 見名知意, 返回一個自適應的 Extension, 說白了就是返回Dubbo經過字符拼接出來的Extension類
下面看看這個 getAdaptiveExtension()
源碼以下:
@SuppressWarnings("unchecked") public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { // todo 爲了線程安全 , 使用了雙重同步鎖 synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { // todo 跟進去 instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
着重跟進 instance = createAdaptiveExtension();
方法, 源碼以下: 主要邏輯以下:
ExtensionClass 能夠直白的理解成 SPI 接口的實現類, 或者是wrapper類
上面的代碼中想要獲取一個 AdaptiveExtensionClass()
那麼問題來了, 從哪裏獲取呢? 跟進getAdaptiveExtensionClass()
沒錯就在下面的
private Class<?> getAdaptiveExtensionClass() { // todo 加載配置文件 getExtensionClasses(); // todo 在前一步加載配置文件時, 加載到了 AdaptiveExtensionFactory, 這裏返回的就是 CachedAdaptiveClass if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 若是沒有手動實現接口的代理類,那麼Dubbo就會自動給你實現一個 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
往下跟進getExtensionClasses();
下面的函數中維護着一個 cachedClasses
它是一個Map , key=String value= Class ; 說白了, 存放的就是從SPI配置文件中讀取配置信息
// 實際上就是將配置中的 key=value 讀取裝在進map中 private Map<String, Class<?>> getExtensionClasses() { // todo cachedClasses是 ExtensionLoader的屬性: Holder<Map<String, Class<?>>> cachedClasses // todo 用於存儲提早約定好了存儲在 類路徑下的 METE-INF/services 以及dubbo原生提供的擴展點 // todo 一樣是雙重同步鎖 + volatile 保證線程安全 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // todo 着重看這個函數 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
進行跟進loadExtensionClasses();
能夠看到, Dubbo會按照約定讀取下面幾個配置文件中的配置信息, 下面我註釋上的文件的全路徑所對應的文件中會記錄Dubbo原生的SPI的實現, 咱們也能遵循這個規則提供本身的實現類
好比隨便查看一個配置文件
// synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); // todo 跟進這個 loadDirectory() 方法, 看看 // todo META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); // todo META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); // todo META-INF/dubbo/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); //todo META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); //todo META-INF/services/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); //todo META-INF/services/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }
下面看一下處理的詳細細節信息:
AdaptiveExtensionFactory.java
暫時緩存起來了SpiExtensionFactory,java
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } // todo 查看有沒有標註 @Adaptive 註解, 若是標註有這個註解的話, 那麼就將他暫時存放起來, 而不是執行下面的邏輯, 構造出對象來 // todo 昌武, 你看, 你在驗證ioc時, 你提供的PersonInterface很顯然是存在這個@Adaptive註解 ,他會在上面提到getAdaptiveClasss() 後 而後newInstance()建立實例 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } }
小結: 到這裏基本上就到了Dubbo的底層確實會去讀取配置文件, 根據他們的配置狀況, 緩存在不一樣容器中
好, 到這裏上面所說的getExtensionClasses();
方法就說完了, 回到下面的方法中
獲得了AdaptiveExtensionFactory
類以後, 接着就經過反射建立的它實例對象, 因此說, 咱們要去看他的構造方法, 以下:
getExtension(Class<T> type, String name)
又看到了這行代碼ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
這行代碼的執行流程其實已經說過了, 此次根據名稱獲取的 Extension是 SpiExtensionFactory, 並將它維護起來
新的問題就來了, 這個SPIExtensionFactory是誰呢? 有啥用呢 看下面, 說白了, 用它處理添加有Dubbo的SPI註解的接口, 而後嘗試獲取這些接口的 實現
構建方法執行完成了, 也就說明 AdaptiveExtension 建立完成了, 剛纔所說的 createAdaptiveExtension
injectExtension其實就是回去作IOC / AOP 相關的操做, 如今咱們跟蹤的實現類是 AdaptiveExtension
它沒有依賴其餘的屬性, 可是我提供的PersonInterface依賴了, 因此說咱們暫時先不進如這個方法,稍後再進去查看他的實現
小結: 下圖是咱們的啓動類, 到目前爲止, 咱們就看完了啓動類的第一行代碼作了什麼, 那它主要是作了哪些事情呢?
下面就繼續看這行extensionLoader.getExtension("human")
, 看他的返回值, 很明顯, 就是要返回咱們須要的personInterface的實現類, 並在這個過程當中穿插這IOC和AOP的邏輯
回顧一下實驗的環境, 從新整理一下思路: 咱們想獲取出 key = human的 PersonInterface的實現類, 這實現類長下面這樣:
public class Human implements PersonInterface { private PersonInterface personInterface; //todo 第一個關注點: 咱們的關注點就是說, Human 會幫咱們將哪個實現類當成入參注入進來呢? //todo 答案是 URL ,dubbo本身封裝的URL, 統一資源定位符, dubbo 會解析入參位置的 url中封裝的map //todo map中的key 與 PersonInteface中的使用 @Adaptive("person") 註解標記的value對應, 那麼值就是將要注入的實際類型 //todo 第二個關注點: dubbo底層極可能是經過反射使用構造方法完成的屬性注入 public void setPersonInterface(PersonInterface personInterface) { this.personInterface = personInterface; } @Override public String getName(URL url) { System.out.println("i am Human "); return "i am Human + " + personInterface.getName(url); } }
能夠很直接的看到, 這個實現類實際上是依賴了一個 PersionInterface的屬性,須要將這個屬性注入給他, 因而問題來了, 注入的是誰呢? 下面繼續往下拉看
進入下面的方法, 主要邏輯以下
咱們先看下: injectExtension(instance)的實現細節:
主要邏輯以下:
objectFactory.getExtension(pt,property)
上圖中的主要目的就是完成依賴注入, 什麼依賴注入呢? 就是在 Human.java中 依賴了一個PersonInterface類型的屬性, Dubbo須要幫我填充上 , HumanInterface.java 中鎖依賴的那個具體的實現類是誰呢? 就是在上面函數中的經過 objectFactory.getExtension(Class,name)
動態生成出來的
當咱們繼續跟進這個getExtension(), 就會發現下面的現象, 看我在下圖中標出來的綠色部分, 能夠發現 , 他獲取出來的 ExtensionLoader全稱以下: 它就是Dubbo咱們生成出來的代理 ExtensionLoader
再進一步, 經過loader 獲取出自適應的拓展類: getAdapativeExtension()
經過反編譯看一下生成的Interface是誰, 能夠看一下,它的實現, 這就是爲何Dubbo經過URL就能知道該注入誰, 用誰取幹活
先說啥是AOP, 就是面向切面編程, 其實說白了就是對現有的對象進行加強
Dubbo是怎麼作的呢? 按照Dubbo的約定, 咱們的這樣編碼:即 經過繼承+構造方法 實現AOP , 就像下面這樣
Dubbo的底層實現: 處理AOP的邏輯在下面
Dubbo會從SPI配置文件中找到咱們添加就進去的Wrapperlei, 經過構造方法反射出他們的實例,, 重要的是將反射出來的這個實例替換成了原來未被加強的 對象, 就跟java的感受就像是升級版的靜態代理同樣
最後打一個小廣告: 我是bloger 賜我白日夢, 本科大三在讀, 熱衷java研發, 指望有一份Java相關實習崗位的工做, 能夠全職實習半年左右, 最理想城市是北京, 求大佬的內推哇