dubbo源碼分析--dubbo spi解析

1. 什麼叫SPI?

簡單總結就是一種使用類名字符串來動態實例化java類的方式,也就是反射。java

2. java SPI與Dubbo SPI有什麼區別

(此圖來自網上,我沒有刻意去截圖)ide

而後在這個文件裏面寫入實現類函數

com.blueskykong.javaspi.serializer.KryoSerializerurl

com.blueskykong.javaspi.serializer.JavaSerializerspa

 

可是dubbo的SPI格式變了,也就意味着不能直接使用java SPI了code

文件的目錄類似,可是裏面的內容變爲了key-valueblog

adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory接口

結論:所謂的這些SPI只不過是一種方式而已,最後都是反射生成類(還有classloader的問題,這裏咱們不討論),組織方式能夠隨便變,可是既然是分析dubbo,那麼這裏就看dubbo是怎麼最終實現的ssl

3. 爲何dubbo要本身實現所謂的SPI?

dubbo至關於使用了key-value的形式,用key來映射每一個類,這樣用戶就能夠在url加入這個key來實現動態加載某個類的需求,字符串

那麼爲何java SPI的方式不行:

  • java SPI的文件裏面僅僅只有類名,若是想讓用戶動態指定,那麼須要按照這個類名來標識用戶想要哪一個類,太長,或者是在類名字上加上必定的區分規則
  • java SPI的加載是一次性會加載全部的類,可是並非每一個類都須要

4. dubbo spi實現方式詳解

4.1 背景

如今使用key-value的方式已經能夠由用戶指定key去加載class,可是出於系統的考慮還有一些功能須要擴展

  • key-value的指定是由@Adaptive和@SPI來輔助實現,@SPI能夠指定類級別的默認的類,@Adaptive能夠設置函數級別須要加載哪一個class
  • 針對FIlter須要指定是否起效,以及在provider起效仍是consumer起效,以及Filter之間的優先級

 4.2 源碼

dubbo中負責SPI的處理的類主要是 ExtensionLoader

這裏我標註出主要的變量含義

/**
     * 這個三個表明須要從哪一個resource路徑下加載咱們的key-value文件
     */
    private static final String SERVICES_DIRECTORY = "META-INF/services/";

    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

    /**
     * 在註解裏面的value是可使用逗號(,)來設置多個值
     */
    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

    /**
     * ExtensionLoader 是每一個接口對應一個 這個map保存了接口和對應的ExtensionLoader的對應
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

    /**
     * 保存了配置文件中全部value-key之間的映射
     */
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

    // 以上都是靜態變量也就是是全局的

    /**
     * 當前這個ExtensionLoader 對應的那個接口
     */
    private final Class<?> type;

    private final ExtensionFactory objectFactory;

    /**
     * value-key之間的反映射
     */
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();

    /**
     * 配置文件中key-value之間的映射 value指的是類class
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();

    /**
     * 這裏保存着這個接口的實現類含有@Activate註解
     */
    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
    /**
     * 這個接口下 key-value的映射 value指的是實例
     */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

    /**
     * 適配類的實例
     */
    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    /***
     * 這個接口的適配類 也就是註解@Adaptive的註解加載類的這個類就是適配類 所謂的適配類 就是實現根據url裏面的參數
     * 來決定使用這個接口的哪一個子類
     */
    private volatile Class<?> cachedAdaptiveClass = null;
    /**
     * SPI註解裏面value 做爲這個接口的默認加載類
     */
    private String cachedDefaultName;

總結一下:

  1. 每個接口好比Protocol和ExtensionLoader之間是一一對應的關,可是Protocol這個接口有不少子類,以及默認類,那麼這些管理都是在ExtensionLoader裏面管理,上面的大多數字段都是爲了管理而存在
  2. ExtensionLoader最主要的功能是爲了根據key來獲取類,而後其餘的都是一些變化。
  3. ExtensionLoader的主要功能和思想(爲了實現key到value的轉換),也就是說我想在有了key-value之間的關係,你能夠認爲這就是一個小型db,那麼你想查詢某一個接口的class有多少,以及每一個class對應的key是什麼,這些都只是功能的問題,核心是我有了關係,只要能從這些元數據推導出來,那麼ExtensionLoader均可以作到。

 

5. 高級

 5.1 包裝類

  在key-value映射中,會分爲包裝類和正常類,所謂的包裝類就是利用裝飾器模式,來實現對原功能進行一些額外的處理,識別方式是按照是否有一個構造方法含有這個接口做爲參數爲標識

 5.2 注入

  這裏的注入是指對接口裏面的變量進行注入實例

 

 

 

還在更新......

相關文章
相關標籤/搜索