Java的SPI機(jī)制以及基于SPI編程示例詳解
背景
在面向?qū)ο蟮脑O(shè)計(jì)原則中,一般推薦模塊之間基于接口編程,通常情況下調(diào)用方模塊是不會(huì)感知到被調(diào)用方模塊的內(nèi)部具體實(shí)現(xiàn)。一旦代碼里面涉及具體實(shí)現(xiàn)類,就違反了開閉原則。如果需要替換一種實(shí)現(xiàn),就需要修改代碼。
為了實(shí)現(xiàn)在模塊裝配的時(shí)候不用在程序里面動(dòng)態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。Java SPI 就是提供了這樣一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。這有點(diǎn)類似 IOC 的思想,將裝配的控制權(quán)移交到了程序之外。
SPI 英文為 Service Provider Interface, 字面意思就是:“服務(wù)提供者的接口”,可以理解為專門提供給服務(wù)調(diào)用者或者擴(kuò)展框架功能的開發(fā)者去使用的一個(gè)接口。
SPI 將服務(wù)接口和具體的服務(wù)實(shí)現(xiàn)分離開來,將服務(wù)調(diào)用方和服務(wù)實(shí)現(xiàn)者解耦,能夠提升程序的擴(kuò)展性、可維護(hù)性。修改或者替換服務(wù)實(shí)現(xiàn)并不需要修改調(diào)用方。
使用場(chǎng)景
很多框架都使用了 Java 的 SPI 機(jī)制,比如:數(shù)據(jù)庫加載驅(qū)動(dòng),日志接口,以及 dubbo 的擴(kuò)展實(shí)現(xiàn)等等。拿日志接口來說,Spring框架提供的日志服務(wù) SLF4J 其實(shí)只是一個(gè)日志接口,但是 SLF4J 的具體實(shí)現(xiàn)可以有幾種,比如:Logback、Log4j、Log4j2 等等,而且還可以切換,在切換日志具體實(shí)現(xiàn)的時(shí)候我們是不需要更改項(xiàng)目代碼的,只需要在 Maven 依賴?yán)锩嫘薷囊恍?pom 依賴就好了。
與API的區(qū)別
API:是指可以用來完成某項(xiàng)功能的類、接口或者方法。提供方提供實(shí)現(xiàn)方式,調(diào)用方只需調(diào)用即可。
SPI:是指用來繼承、擴(kuò)展,完成自定義功能的類、接口或者方法。調(diào)用方可選擇使用提供方提供的內(nèi)置實(shí)現(xiàn),也可以自己實(shí)現(xiàn)。
SPI原理
Java SPI的具體約定為:當(dāng)服務(wù)的提供者提供了服務(wù)接口后,在jar包的META-INF/services目錄下同時(shí)創(chuàng)建一個(gè)以服務(wù)接口全類名命名的文件,該文件的內(nèi)容就是實(shí)現(xiàn)該服務(wù)接口具體實(shí)現(xiàn)類的全名,當(dāng)然這個(gè)服務(wù)接口實(shí)現(xiàn)類也必須在這個(gè)jar中。當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過該jar包META-INF/services下的配置文件找到具體的實(shí)現(xiàn)類,并裝載實(shí)例化,完成模塊的注入。Java就是通過掃描META-INF/services文件夾目錄下面的文件,把實(shí)現(xiàn)類加載到servciceLoader里面。
簡(jiǎn)單來講,META-INF/services/下的配置文件名字就是接口的全類名,實(shí)現(xiàn)類的全類名就是問這個(gè)文件的內(nèi)容,如果有多個(gè)
實(shí)現(xiàn)類,可以全部寫在這個(gè)文件中,同時(shí)在實(shí)現(xiàn)類中要實(shí)現(xiàn)這個(gè)接口。
不定義在META-INF/services下面行不行?就想定義在別的地方可以嗎?
答案是不行。看下圖JDK源碼中,因?yàn)橐呀?jīng)定義為配置路徑,如果寫在別的地方,類加載器就會(huì)找不到了。
案例實(shí)現(xiàn)
代碼結(jié)構(gòu)如下:
├─main
│ ├─java
│ │ └─com
│ │ ├─test
│ │ │ └─Apple
│ │ │ └─Fruit
│ └─resources
│ ├─META-INF
│ │ └─services
│ │ └─com.test.Fruit
- 創(chuàng)建接口Fruit,模擬服務(wù)提供方接口
package com.test; public interface Fruit { }
- 創(chuàng)建接口實(shí)現(xiàn)類Apple,實(shí)現(xiàn)Fruit接口;
package com.test; public class Apple implements Fruit { static { System.out.println("hi,I am an apple!"); } }
- 在resources結(jié)構(gòu)下創(chuàng)建META-INF/services目錄,在這個(gè)目錄下,創(chuàng)建以這個(gè)接口名命名的文件com.test.Fruit,同時(shí)在這個(gè)文件中寫入這個(gè)實(shí)現(xiàn)類的全類名;
com.test.Apple
- 創(chuàng)建Test測(cè)試類,在測(cè)試類中創(chuàng)建一個(gè)類加載器ServiceLoader來實(shí)現(xiàn)本案例。
import com.test.Fruit; import java.util.ServiceLoader; public class Test { public static void main(String[] args) { ServiceLoader<Fruit> test = ServiceLoader.load(Fruit.class); for (Fruit item:test){ } } }
執(zhí)行結(jié)果如下,表明本案例成功執(zhí)行。
hi,I am an apple!
以上就是Java的SPI機(jī)制以及基于SPI編程示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java SPI機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java8生成時(shí)間方式及格式化時(shí)間的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Java8生成時(shí)間方式及格式化時(shí)間的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java面試崗常見問題之ArrayList和LinkedList的區(qū)別
ArrayList和LinkedList作為我們Java中最常使用的集合類,很多人在被問到他們的區(qū)別時(shí),憋了半天僅僅冒出一句:一個(gè)是數(shù)組一個(gè)是鏈表。這樣回答簡(jiǎn)直讓面試官吐血。為了讓兄弟們打好基礎(chǔ),我們通過實(shí)際的使用測(cè)試,好好說一下ArrayList和LinkedList的區(qū)別這道經(jīng)典的面試題2022-01-01Spring中OpenFeign的使用方法最佳實(shí)踐
這篇文章主要介紹了Spring中OpenFeign使用的相關(guān)資料,OpenFeign是一個(gè)聲明式的WebService客戶端,簡(jiǎn)化了微服務(wù)之間的調(diào)用,類似于Controller調(diào)用Service,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02springboot+vue項(xiàng)目從第一行代碼到上線部署全流程
本文詳細(xì)介紹了如何從零開始搭建一個(gè)基于Spring Boot和Vue.js的前后端分離項(xiàng)目,并涵蓋項(xiàng)目需求分析、技術(shù)選型、項(xiàng)目結(jié)構(gòu)設(shè)計(jì)、前后端交互、部署上線等全過程,感興趣的朋友跟隨小編一起看看吧2024-11-11MyBatisPlus 主鍵策略的實(shí)現(xiàn)(4種)
MyBatis Plus 集成了多種主鍵策略,幫助用戶快速生成主鍵,本文主要介紹了MyBatisPlus主鍵策略的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10利用session實(shí)現(xiàn)簡(jiǎn)單購物車功能
這篇文章主要為大家詳細(xì)介紹了利用session實(shí)現(xiàn)簡(jiǎn)單購物車功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Java實(shí)現(xiàn)十秒向MySQL插入百萬條數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)十秒向MySQL插入百萬條數(shù)據(jù),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定借鑒價(jià)值,需要的可以參考一下2022-11-11SpringBoot?MongoCustomConversions自定義轉(zhuǎn)換方式
這篇文章主要介紹了SpringBoot?MongoCustomConversions自定義轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08