Java SPI簡單應(yīng)用案例詳解
開篇
本文主要談一下 Java SPI(Service Provider Interface) ,因?yàn)樽罱诳?Dubbo 的相關(guān)內(nèi)容,其中涉及到了 一個(gè)概念- Dubbo SPI, 最后又牽扯出來了 JAVA SPI, 所以先從 Java SPI 開整。
正文
平常學(xué)習(xí)一個(gè)知識(shí)點(diǎn),我們的常規(guī)做法是:
- 是什么
- 有什么用
- 怎么用
這次我們倒著做,先不談什么是 SPI 及其作用,來看下如何使用。
使用
1. 創(chuàng)建一個(gè) maven 工程
2. 創(chuàng)建一個(gè)接口類以及實(shí)現(xiàn)類
// 接口 public interface HelloService { void sayHello(); } // 實(shí)現(xiàn)類 1 public class HelloServiceImpl1 implements HelloService { @Override public void sayHello() { System.out.println("hello impl1"); } } // 實(shí)現(xiàn)類 2 public class HelloServiceImpl2 implements HelloService { @Override public void sayHello() { System.out.println("hello impl2"); } }
3. 創(chuàng)建一個(gè) META-INF/services 文件夾,并添加一個(gè)文件
在 classpath 下面創(chuàng)建一個(gè)META-INF/services目錄,再在下面創(chuàng)建一個(gè)
以接口類全路徑名 命名的文件
即:com.nimo.spidemo.service.HelloService
4. 在第三步創(chuàng)建的文件中寫入如下內(nèi)容
寫入兩個(gè)實(shí)現(xiàn)類的全路徑名
com.nimo.spidemo.service.impl.HelloServiceImpl1 com.nimo.spidemo.service.impl.HelloServiceImpl2
5. 啟動(dòng)函數(shù)
public class App { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 方式 一 Iterator<HelloService> providers = Service.providers(HelloService.class); while(providers.hasNext()) { HelloService ser = providers.next(); ser.sayHello(); } System.out.println("-----------------分割線---------------"); // 方式 二 ServiceLoader<HelloService> load = ServiceLoader.load(HelloService.class); Iterator<HelloService> iterator = load.iterator(); while(iterator.hasNext()) { HelloService ser = iterator.next(); ser.sayHello(); } } }
運(yùn)行結(jié)果如下:
hello impl1
hello impl2
-----------------分割線---------------
hello impl1
hello impl2
使用要素
針對上面的 demo,可以看出使用 Java SPI 的幾個(gè)關(guān)鍵要素:
- 接口類,比如 HelloService
- 對應(yīng)接口的實(shí)現(xiàn)類
實(shí)現(xiàn)類必須攜帶一個(gè)不帶參數(shù)的構(gòu)造方法 - 文件夾 META-INF/services
放置 classpath 目錄下 - 以“接口全限定名”命名的文件
- 文件內(nèi)容為接口實(shí)現(xiàn)類的全路徑
主程序通過java.util.ServiceLoder掃描META-INF/services下的配置文件,然后會(huì)找到實(shí)現(xiàn)類的全限定名,最后把類加載到JVM;
SPI 的作用
從上面的 demo 中可以看到,Java SPI 就是把某個(gè)接口的 指定實(shí)現(xiàn)類 (通過在指定文件配置的方式)給實(shí)例化出來了。
準(zhǔn)確+官方的說:
SPI 是一種將服務(wù)接口與服務(wù)實(shí)現(xiàn)分離以達(dá)到解耦、大大提升了程序可擴(kuò)展性的機(jī)制。引入服務(wù)提供者就是引入了 spi 接口的實(shí)現(xiàn)者,通過本地的注冊發(fā)現(xiàn)獲取到具體的實(shí)現(xiàn)類,輕松可插拔。
~~~ 如果還是不懂就接著往下看⬇️
SPI 的應(yīng)用場景
一個(gè)典型的案例就是 jdbc 。
數(shù)據(jù)庫各大廠商(如Mysql、Oracle)會(huì)根據(jù)一個(gè)統(tǒng)一的接口規(guī)范( java.sql.Driver )開發(fā)各自的驅(qū)動(dòng)實(shí)現(xiàn)邏輯。
客戶端使用 jdbc 時(shí)不需要去改變代碼,直接引入不同的 spi 接口服務(wù)即可。
例如 :
Mysql 是 com.mysql.jdbc.Drive
Oracle 是 oracle.jdbc.driver.OracleDriver
一段熟悉的代碼:
使用操作 mysql 數(shù)據(jù)庫的前置工作:
//1.加載驅(qū)動(dòng)程序 Class.forName("com.mysql.jdbc.Driver"); //2. 獲得數(shù)據(jù)庫連接 Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
當(dāng)我們需要切到 oracle 數(shù)據(jù)庫時(shí),更改驅(qū)動(dòng)為 oracle.jdbc.driver.OracleDriver, 最后修改連接信息【用戶名,密碼等】即可。
總結(jié)
Java SPI 是一種服務(wù)發(fā)現(xiàn)機(jī)制。它通過在 classPath 路徑下的 META-INF/services 文件夾查找文件,自動(dòng)加載文件里所定義的類。
它的核心關(guān)鍵作用就是 擴(kuò)展。
其他應(yīng)用場景:
- 日志門面接口實(shí)現(xiàn)類加載
SLF4J加載不同提供商的日志實(shí)現(xiàn)類 - Spring
Spring中大量使用了 SPI,比如:對 servlet3.0 規(guī)范 ServletContainerInitializer 的實(shí)現(xiàn)、自動(dòng)類型轉(zhuǎn)換Type Conversion SPI(Converter SPI、Formatter SPI)等 - Dubbo
Dubbo中也大量使用SPI的方式實(shí)現(xiàn)框架的擴(kuò)展, 不過它對Java提供的原生SPI做了封裝,允許用戶擴(kuò)展實(shí)現(xiàn)Filter接口
其中 Dubbo 中的 SPI 是接下來研究的重點(diǎn)。
到此這篇關(guān)于Java SPI簡單應(yīng)用案例詳解的文章就介紹到這了,更多相關(guān)Java SPI簡單應(yīng)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java ArrayList的基本概念和作用及動(dòng)態(tài)數(shù)組的機(jī)制與性能
在Java中,ArrayList是一個(gè)實(shí)現(xiàn)了List接口的動(dòng)態(tài)數(shù)組,它可以根據(jù)需要自動(dòng)增加大小,因此可以存儲(chǔ)任意數(shù)量的元素,這篇文章主要介紹了探秘Java ArrayList的基本概念和作用及動(dòng)態(tài)數(shù)組的機(jī)制與性能,需要的朋友可以參考下2023-12-12Java實(shí)現(xiàn)Timer的定時(shí)調(diào)度函數(shù)schedule的四種用法
本文主要介紹了Java實(shí)現(xiàn)Timer的定時(shí)調(diào)度函數(shù)schedule的四種用法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Java中new Date().getTime()指定時(shí)區(qū)的時(shí)間戳問題小結(jié)
本文主要介紹了Java中new Date().getTime()時(shí)間戳問題小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Spring bean的實(shí)例化和IOC依賴注入詳解
這篇文章主要介紹了Spring bean的實(shí)例化和IOC依賴注入詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03詳解Spring-Boot集成Spring session并存入redis
這篇文章主要介紹了詳解Spring-Boot集成Spring session并存入redis,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例
這篇文章主要介紹了Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java類加載器與雙親委派機(jī)制和線程上下文類加載器專項(xiàng)解讀分析
類加載器負(fù)責(zé)讀取Java字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例的代碼模塊。本文主要和大家聊聊JVM類加載器ClassLoader的使用,需要的可以了解一下2022-12-12