title: SPI
tags: JDK,SPI,服務(wù)發(fā)現(xiàn)
grammar_cjkRuby: true
什么是SPI
SPI,JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,簡(jiǎn)單來(lái)說(shuō),就是一種動(dòng)態(tài)替換機(jī)制。比如說(shuō),定義了一種規(guī)范,需要廠商去實(shí)現(xiàn)。對(duì)于應(yīng)用方來(lái)說(shuō),只需將對(duì)應(yīng)廠商的實(shí)現(xiàn)集成進(jìn)來(lái),就可以實(shí)現(xiàn)對(duì)該規(guī)范的實(shí)現(xiàn),是一種插拔式的拓展手段。
為什么需要SPI
在面向?qū)ο蟮脑O(shè)計(jì)中,一般基于接口編程,模塊之間不會(huì)對(duì)實(shí)現(xiàn)類進(jìn)行硬編碼,一旦涉及某一個(gè)實(shí)現(xiàn)類,需要?jiǎng)討B(tài)指定或者修改該實(shí)現(xiàn)類,就要修改代碼,就違反了可插拔的原則。而SPI的出現(xiàn),彌補(bǔ)了這種問(wèn)題。具體實(shí)現(xiàn)在模塊之間無(wú)需動(dòng)態(tài)指定,只需傳進(jìn)對(duì)應(yīng)接口類型,就可以動(dòng)態(tài)找到對(duì)應(yīng)的實(shí)現(xiàn)類
如何實(shí)現(xiàn)SPI
SPI實(shí)現(xiàn)需要遵循以下規(guī)范:
- 需要在classpath路徑創(chuàng)建一個(gè)文件夾,名為META-INF/services
- 需要在META-INF/services創(chuàng)建一個(gè)文件:
- 文件名為該接口的全路徑名稱
- 文件內(nèi)容為該接口實(shí)現(xiàn)類的全路徑
- 文件編碼必須是UTF-8
- 通過(guò)java.util.ServiceLoader加載機(jī)制來(lái)獲取
SPI實(shí)踐
定義接口
public interface DataBaseDriver {
String connect(String host);
}
實(shí)現(xiàn)接口
package com.lung.spi.mysql;
import com.lung.spi.api.DataBaseDriver;
/**
* @Author: liumenglong
* @Date: 2018/9/3 10:31
* @Description:
*/
public class MysqlDriver implements DataBaseDriver {
public String connect(String s) {
return "Mysql Driver : "+s;
}
}
配置文件


發(fā)現(xiàn)服務(wù)

SPI的優(yōu)點(diǎn)和缺點(diǎn)
優(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ò)展或替換框架組件。
缺點(diǎn):
雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過(guò)遍歷全部獲取,也就是接口的實(shí)現(xiàn)類全部加載并實(shí)例化一遍。如果你并不想用某些實(shí)現(xiàn)類,它也被加載并實(shí)例化了,這就造成了浪費(fèi)。獲取某個(gè)實(shí)現(xiàn)類的方式不夠靈活,只能通過(guò)Iterator形式獲取,不能根據(jù)某個(gè)參數(shù)來(lái)獲取對(duì)應(yīng)的實(shí)現(xiàn)類。
多個(gè)并發(fā)多線程使用ServiceLoader類的實(shí)例是不安全的。