http://www.itdecent.cn/p/46b42f7f593c
1 SPI是什么
SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實(shí)現(xiàn)或者擴(kuò)展的API,它可以用來啟用框架擴(kuò)展和替換組件。
Java SPI 實(shí)際上是“基于接口的編程+策略模式+配置文件”組合實(shí)現(xiàn)的動(dòng)態(tài)加載機(jī)制。
系統(tǒng)設(shè)計(jì)的各個(gè)抽象,往往有很多不同的實(shí)現(xiàn)方案,在面向的對(duì)象的設(shè)計(jì)里,一般推薦模塊之間基于接口編程,模塊之間不對(duì)實(shí)現(xiàn)類進(jì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)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要。所以SPI的核心思想就是解耦。
總結(jié)
優(yōu)點(diǎn):
使用Java SPI機(jī)制的優(yōu)勢(shì)是實(shí)現(xiàn)解耦,使得第三方服務(wù)模塊的裝配控制的邏輯與調(diào)用者的業(yè)務(wù)代碼分離,而不是耦合在一起。應(yīng)用程序可以根據(jù)實(shí)際業(yè)務(wù)情況啟用框架擴(kuò)展或替換框架組件。
相比使用提供接口jar包,供第三方服務(wù)模塊實(shí)現(xiàn)接口的方式,SPI的方式使得源框架,不必關(guān)心接口的實(shí)現(xiàn)類的路徑,可以不用通過下面的方式獲取接口實(shí)現(xiàn)類:
- 代碼硬編碼import 導(dǎo)入實(shí)現(xiàn)類
- 指定類全路徑反射獲取:例如在JDBC4.0之前,JDBC中獲取數(shù)據(jù)庫(kù)驅(qū)動(dòng)類需要通過Class.forName("com.mysql.jdbc.Driver"),類似語(yǔ)句先動(dòng)態(tài)加載數(shù)據(jù)庫(kù)相關(guān)的驅(qū)動(dòng),然后再進(jìn)行獲取連接等的操作
- 第三方服務(wù)模塊把接口實(shí)現(xiàn)類實(shí)例注冊(cè)到指定地方,源框架從該處訪問實(shí)例
通過SPI的方式,第三方服務(wù)模塊實(shí)現(xiàn)接口后,在第三方的項(xiàng)目代碼的META-INF/services目錄下的配置文件指定實(shí)現(xiàn)類的全路徑名,源碼框架即可找到實(shí)現(xiàn)類
缺點(diǎn):
- 雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實(shí)現(xiàn)類全部加載并實(shí)例化一遍。如果你并不想用某些實(shí)現(xiàn)類,它也被加載并實(shí)例化了,這就造成了浪費(fèi)。獲取某個(gè)實(shí)現(xiàn)類的方式不夠靈活,只能通過Iterator形式獲取,不能根據(jù)某個(gè)參數(shù)來獲取對(duì)應(yīng)的實(shí)現(xiàn)類。
- 多個(gè)并發(fā)多線程使用ServiceLoader類的實(shí)例是不安全的。
參考
Java核心技術(shù)36講
The Java? Tutorials
Java Doc
Service Provider Interface: Creating Extensible Java Applications
Service provider interface
Java ServiceLoader使用和解析
Java基礎(chǔ)之SPI機(jī)制
Java中SPI機(jī)制深入及源碼解析
SPI機(jī)制簡(jiǎn)介