Dubbo高級進階Spi應用以及與JDK的Spi區別


Dubbo官網

DubboSpi

Dubbo高級進階Spi應用

Dubbo是由阿里巴巴開源的一款高性能、輕量級的開源Java Rpc(遠程過程調用)框架,提供三大核心能力:面向接口的遠程方法調用、智能容錯和負載均衡、服務自動注冊與發現。

在Dubbo的源碼中,下面這種句式出現比較多,比如如下句式:通過ExtensionLoader獲取Protocol接口的代理類。

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();   

仔細翻看dubbo中的源碼,Protocol接口的實現類有很多種,那么在程序的執行中怎么得到對應的實現類,怎么去動態的擴展接口實現,這些問題就是今天討論的重點。

一、Spi是什么。

Spi全稱為(Service Provider Interface),是Jdk內置的一種服務提供發現機制,目前有不少框架用他來做服務的拓展發現,簡單說Spi是一種動態替換發現機制,使用Spi機制的優勢是實現解耦,使第三方服務模塊的裝配控制邏輯與調用者的業務代碼分離。

二、Java中JDK的Spi實現。

  Java中如果想要使用SPI功能,先提供標准服務接口,然后在提供相關接口實現的調用者,這樣就可以通過spi機制中約定好的信息進行查詢相應的接口實現

  SPI遵循以下約定

  1)當服務提供者提供了一個服務(接口)的具體實現后,在classpath下的META-INF/services目錄下創建一個以“接口全限定名”命名的文件,內容為實現類的全限定名。

  2)接口實現類所在的jar包放在駐車鞥徐的classpath中;

  3)主程序通過java.util.ServiceLoader動態狀態實現模塊(類),它通過掃描META-INF/services目錄下的配置文件找到實現類的全限定名,把類加載到JVM。

  4)Spi的實現類必須攜帶一個無參構造

  JDK中的Spi機制應用比較廣泛比如說common-logging、JDBC等,這里使用JDBC進行舉例

1、JDBC接口定義
  首先在java 中定義接口java.sql.Dirver並沒有具體的實現,具體的實現由不同的廠商來提供
2、mysql實現
  在mysql的jar包mysql-connector-java-6.0.6.jar中,可以找到META-INF/services目錄,該目錄下會有一個名字為java.sql.Driver的文件,文件內容是com.mysql.cj.jdbc.Driver,這里面的內容就是針對Java中定義的接口的實現。
3、查找調用在DriverManager類的靜態代碼塊 loadInitialDrivers();方法中

  感興趣的小伙伴可以去翻看源碼哈~這里我就不展示源碼了,做一個簡單的模擬案例進行展示,項目目錄如下所示

 

 

源碼如下:

 Driver類

package city.albert;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:09 PM
 */
public interface Driver {

    /**
     * 模擬獲取驅動名稱
     * @return
     */
    String driverName();
}
View Code

MysqlDriver實現類

package city.albert.impl;

import city.albert.Driver;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:12 PM
 */
public class MysqlDriver implements Driver {
    @Override
    public String driverName() {
        return "mysql 驅動";
    }
}
View Code

OricalDriver實現類

package city.albert.impl;

import city.albert.Driver;
/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:14 PM
 */
public class OricalDriver implements Driver{
    @Override
    public String driverName() {
        return "orical 驅動";
    }
}
View Code

項目的classpath下META-INF/services目錄下創建名為“city.albert.Driver”的文件夾指定你的實現類即可,我這里兩個類同時指定內容為:

city.albert.impl.MysqlDriver
city.albert.impl.OricalDriver

測試類如下

package city.albert.impl;

import city.albert.Driver;

import java.util.ServiceLoader;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:17 PM
 */
public class TestMain {

    public static void main(String[] args) {
        ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
        for (Driver driver : drivers) {
            System.out.println(driver.driverName());
        }
    }
}

結果兩個實現類都被加載

三、Dubbo中的Spi實現。

  區別:在Dubbo的框架中並沒有使用JDK的Spi來實現,而是重寫了Spi的實現,Dubbo中的Spi形式上都是從Jar中加載對應的拓展類,但是Dubbo支持更多的加載路徑,也不是通過Iterator的形式調用而是通過名稱來定位具體的Provider,按照需要進行加載,並非Jdk中的一次性全部加載,效率更高,同時支持Provider以類似IOC的形式提供。

Dubbo自己實現Spi的目的

1、JDK標准的SPI會一次性實例化拓展點的所有實現,如果所有的實現初始化很耗時,並沒加載上也沒有用,就會很浪費資源。
2、如果有的拓展點加載失敗,則所有的拓展點無法使用。
3、提供了對拓展點包裝的功能(Adaptive),並且還支持Set的方式對其他拓展點進行注入

  Dubbo中實現Spi與JDK形式上的區別

  1、接口上需要添加“org.apache.dubbo.common.extension”包下的@SPI注解,在@SPI("spiService")注解中可以指定默認拓展點。

  2、在META-INF/dubbo目錄下創建全限定名文件

  3、全限定名文件中的內容是KEY-VALUE形式,value是實現類的全限定類名

  4、調用者使用ExtensionLoader獲取加載

具體實現代碼

api項目中需要引入bubbo依賴因為使用到@Spi注解,版本號根據自己的選型來定哈~

     <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.5</version>
     </dependency>

api代碼

package city.albert;

import org.apache.dubbo.common.extension.SPI;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:49 PM
 */
@SPI
public interface DubboSpiService {
    String getName();
}
View Code

實現類

package city.albert.impl;

import city.albert.DubboSpiService;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:51 PM
 */
public class DubboSpiServiceImpl implements DubboSpiService{
    @Override
    public String getName() {
        return "dubbo——實現類——getName";
    }
}
View Code

META-INF/dubbo/city.albert.DubboSpiService文件內容

spiService = city.albert.impl.DubboSpiServiceImpl

調用類main

package city.albert;

import org.apache.dubbo.common.extension.ExtensionLoader;

/**
 * @author niunafei
 * @function
 * @email niunafei0315@163.com
 * @date 2020/8/25  5:53 PM
 */
public class DubboMain {
    public static void main(String[] args) {
        ExtensionLoader<DubboSpiService> loader = ExtensionLoader.getExtensionLoader(DubboSpiService.class);
        DubboSpiService service = loader.getExtension("spiService");

        System.out.println(service.getName());
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM