dubbo源碼分析3(dubbo中的spi機制)

  上一篇我們看過了jdk中的spi機制,也分析了它的缺點就是會一次性將META-INF/services下的配置文件中,對應接口的全部實現類都給加載;

  而dubbo中的spi肯定是提高了性能,還擴展了原生的spi(這就是一句廢話,如果提高性能和沒有擴展新的功能,幹嘛不用原生的啊(-_-メ))

1. 基於dubbo的spi栗子

  我們先說說提高性能,一般提高性能肯定就是使用到了緩存嘛,沒有什麼比緩存更能提高性能的了;

  啥也不管,先舉個例子看一看,在上一篇的基礎上,導入dubbo依賴

  <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.5</version>
        </dependency>

 

  目錄結構如下:

 

  一個接口和兩個實現類:

@SPI
public interface ISayNameDubbo {
    void say();
}



public class SayChineseNameDubbo implements ISayNameDubbo {
    @Override
    public void say() {
        System.out.println("dubbo 中文:哈嘍,你好帥呀๑乛◡乛๑");
    }
}



public class SayEnglishNameDubbo implements ISayNameDubbo {
    @Override
    public void say() {
        System.out.println("Dubbo English:hello cool java boy");
    }
}

 

  配置文件:

 

  執行結果:

 

  使用方法跟java原生的spi幾乎一樣,只不過有兩個地方需要注意:

  1.接口要加上@SPI註解

  2.配置文件是放在META-INF/dubbo下面,而且文件內容是以鍵值對的形式

 

 2. dubbo中spi機制分析

  根據上面的例子, 我們知道首先使用ExtensionLoader.getExtensionLoader(ISayNameDubbo.class)獲取到指定接口的ExtensionLoader,我們看看這個ExtensionLoader是個啥?╮(╯_╰)╭

  下圖中緩存的操作,可以看到ExtensionLoader其實就是對我們接口的封裝,然後調用這個封裝類的方法,獲取該接口我們指定的實例

  

 

  先看看校驗是否有SPI註解的方法:withExtensionAnnotation  

  下圖所示,其實就是做了一個判斷,後面我們會再看看這個註解中的值,還有啥所用

 

  接下來我們就看一下ExtensionLoader的getExtension方法,就能大概知道是怎麼獲取指定的實例的了;

  其實也可以根據之前使用的java的SPI機制猜一下,大概的思路就是先去META-INF目錄下找到指定的配置文件,然後根據getExtension(String key)方法傳入的key去找到對應的實現類的全路徑,最後使用反射進行實例化就完成了

  然後我們看看代碼是不是這樣實現的

 

  首先我們看看getOrCreateHolder方法,其實使用了一個緩存,用於緩存key->Holer, 至於Holder的實例作用,你點開看看就知道它裏面有個volatile關鍵字,很明顯這裏是爲了協助我們使用單例模式的,保證一個SPI修飾的接口的實現類是單例的;

  注意,下圖這種緩存的操作後面能看到很多(所以說dubbo中的SPI提升了性能๑乛◡乛๑)

 

  我們再接着看createExtension方法,我們重點看看getExtensionClasses()方法

 

 

 

  下面就是加載那三個路徑下,實現類的配置文件,以key->value的形式保存起來

 

 

 

 

  如果想繼續看看怎麼解析配置文件的,可以看看loadDirectory()方法:

 

 

 

3.總結一下

  其實還有很多東西沒說,比如dubbo中的ioc和aop,其實能說很多的,但是我這裏就是不要說了,先不要太深入了;

  總結一下,dubbo的SPI的邏輯幾乎都在ExtensionLoader中,而且一個@SPI修飾的接口,對應這一個ExtensionLoader實例,我們再根據配置文件中的key,調用ExtensionLoader實例的getExtension方法,這裏會使用緩存和Holder來控制實例化,使得每個接口的實現類只會被實例化一次;

  在實例化的時候調用createExtension()方法,這裏會首先加載"META-INF/services/","META-INF/dubbo/","META-INF/dubbo/internal/"三個目錄中的所有配置文件,解析出來所有的key->value,並且將value使用Class.forName方法轉爲Class實例,保存到緩存中,並且根據返回我們需要的Class對象,使用反射實例化對象;

  此時對應的接口的實例化對象已經好了,但是對象裏面的屬性值還是空的,所以還需要使用dubbo的IOC機制,有興趣的可以看看injectExtension方法,objectFactory就是IOC對象(類似spring的IOC容器,可以提供很多的對象),並且還用了AOP的機制,這句代碼:instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));

  不過我的建議是暫時先不要管IOC和AOP,等了解多了再回頭看看,會容易理解很多

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章