八股文背多了,相信大家都聽說過一個(gè)詞, SPI擴(kuò)展 。
有的面試官就很喜歡問這個(gè)問題,SpringBoot的自動裝配是如何實(shí)現(xiàn)的?
基本上,你一說是基于spring的SPI擴(kuò)展機(jī)制,再把spring.factories
文件和EnableAutoConfiguration
提一下,那么這個(gè)問題就答的八九不離十了。
就像四五年前,我去面試的時(shí)候被問到這個(gè)問題,SPI動態(tài)擴(kuò)展機(jī)制這幾個(gè)詞從嘴里一說出來,就把面試官唬的一愣一愣的。可能他們也沒見過這么能裝逼的,一句話能簡簡單單說明白,非要拽一個(gè)聽上去很高大上的詞。
話說回來,被唬住的可不止是面試官,其實(shí)還有我自己。至于SPI擴(kuò)展究竟是個(gè)啥,是怎么實(shí)現(xiàn)的,我當(dāng)時(shí)也根本不明白。
不過現(xiàn)在的面試就是這樣,對線八股文,要想唬住面試官,就得先唬住自己。
那么我們今天暫且不提spring的SPI擴(kuò)展,先來看看java本身自帶的SPI擴(kuò)展機(jī)制是怎么一回事。
1、簡介
SPI的全稱是Service Provider Interface
,翻譯過來就是 服務(wù)提供者的接口 ,它所實(shí)現(xiàn)的其實(shí)是一種服務(wù)的發(fā)現(xiàn)機(jī)制。
這么說起來可能還是有點(diǎn)不好理解,我舉個(gè)例子來類比一下。
在spring項(xiàng)目中,寫service層代碼前,會約定俗成的會添加一個(gè)接口層。然后通過spring中的依賴注入,可以借助@Autowired
等方式注入這個(gè)接口的實(shí)現(xiàn)類的實(shí)例對象,之后對于service的調(diào)用一般也基于接口操作。
簡單形容就是這樣的:
如圖所示,接口、實(shí)現(xiàn)類都是由服務(wù)提供方提供,我們可以把controller看作服務(wù)調(diào)用者,調(diào)用方只管調(diào)用接口就可以了。
雖然也有聲音認(rèn)為,大部分情況下service只有一個(gè)實(shí)現(xiàn)類,接口層顯得有些多余。但是在《Head First Design Patterns》這本書中,大佬們還是建議過:
Program to an interface, not an implementation.
沒錯(cuò),就是常說的 要面向接口編程 。至于好處,也不外乎是降低耦合度、方便日后擴(kuò)展、提高了代碼的靈活性和可維護(hù)性等等。
在上面這個(gè)例子里,這個(gè)接口層和其中的方法我們可以稱之為 API ,而我們要討論的SPI和它相比,有類似也有差異,還是先看圖:
簡單來說,就是服務(wù)的調(diào)用方定義一個(gè)接口規(guī)范,可以由不同的服務(wù)提供者實(shí)現(xiàn)。并且,調(diào)用方能夠通過某種機(jī)制來發(fā)現(xiàn)服務(wù)提供方,并通過接口調(diào)用它的能力。
通過對比,我們可以看出它們雖然都有著接口這一層面,但還是有很大的不同:
API中的接口是服務(wù)提供者給服務(wù)調(diào)用者的一個(gè)功能列表,而SPI中更多強(qiáng)調(diào)的是,服務(wù)調(diào)用者對服務(wù)實(shí)現(xiàn)的一種約束,服務(wù)提供者根據(jù)這種約束實(shí)現(xiàn)的服務(wù),可以被服務(wù)調(diào)用者發(fā)現(xiàn)。
說白了,Java中的SPI實(shí)現(xiàn)的就是,你按我的接口規(guī)范實(shí)現(xiàn)服務(wù),我就能通過某種機(jī)制為這個(gè)接口尋找到這個(gè)服務(wù)。
這么說起來可能還有些抽象,下面我們舉一個(gè)例子,類比具體描述一下這個(gè)過程。
2、定義接口
說起智能家居系統(tǒng),大家現(xiàn)在都比較熟悉了,只要是相同品牌下的產(chǎn)品,連上wifi就能夠通過手機(jī)app控制了,非常方便。
雖然產(chǎn)品不斷更新?lián)Q代,型號更新層出不窮,但是同種家電在app上操作起來,功能一般都是一樣的。就拿空調(diào)來說,我們在app上操作起來一般也就三個(gè)主要功能: 開關(guān) , 選模式 , 調(diào)節(jié)溫度 。
假設(shè)我現(xiàn)在在客廳、臥室、書房安裝了3款不同型號的空調(diào),并把它們都接入到了我app中,那么之后的操作都是相同的幾個(gè)按鍵,簡單粗暴。
思考一下,無論是開關(guān)還是調(diào)溫,都是通過app去調(diào)用設(shè)備的接口罷了,那么如果不同型號的空調(diào)各寫各的接口,后端app在開發(fā)的時(shí)候光對接接口都麻煩的要死。
解決方法也很簡單,我先定義一套接口規(guī)范,不管你以后什么型號的空調(diào),都按我的規(guī)范來實(shí)現(xiàn)接口。以后只要我能發(fā)現(xiàn)你的設(shè)備,那么都可以按相同的方法來調(diào)用接口。
那么下面就先來定義這么一套接口規(guī)范,如果你以后想要接入智能家居系統(tǒng),那么就要遵循這個(gè)規(guī)范來開發(fā)接口。
新建一個(gè)項(xiàng)目作為標(biāo)準(zhǔn),就叫aircondition-standard
好了,然后創(chuàng)建一個(gè)接口。除了3個(gè)操作以外,我們再添加一個(gè)獲取空調(diào)型號的方法。
public interface IAircondition {
// 獲取型號
String getType();
// 開關(guān)
void turnOnOff();
// 調(diào)節(jié)溫度
void adjustTemperature(int temperature);
// 模式變更
void changeModel(int modelId);
}
這個(gè)接口后面要給服務(wù)的實(shí)現(xiàn)方來使用,用maven把它打成jar包:
mvn clean install
之后服務(wù)提供者在項(xiàng)目中就可以引入這個(gè)jar包了,有了這套規(guī)范,就保證了產(chǎn)品后期不管怎么更新?lián)Q代,都能接入到系統(tǒng)來。
3、服務(wù)實(shí)現(xiàn)
制定并發(fā)布完規(guī)則后,掛式空調(diào)作為第一個(gè)服務(wù)提供者就來了,新建一個(gè)項(xiàng)目aircondition-hanging-type
,并引入剛才打好的jar包:
<dependency>
<groupId>com.cn.hydra<span class="hljs-name"groupId>
<artifactId>aircondition-standard<span class="hljs-name"artifactId>
<version>1.0-SNAPSHOT<span class="hljs-name"version>
<span class="hljs-name"dependency>
創(chuàng)建服務(wù)類,并實(shí)現(xiàn)前面定義的接口:
public class HangingTypeAircondition
implements IAircondition{
public String getType() {
return "HangingType";
}
public void turnOnOff() {
System.out.println("掛式空調(diào)開關(guān)");
}
public void adjustTemperature(int i) {
System.out.println("掛式空調(diào)調(diào)節(jié)溫度");
}
public void changeModel(int i) {
System.out.println("掛式空調(diào)更換模式");
}
}
在項(xiàng)目的resources
的目錄下,創(chuàng)建META-INF/services
目錄,然后以前面定義的接口名com.cn.hydra.IAircondition
創(chuàng)建文件,并在文件中寫入實(shí)現(xiàn)類的全限定名。
com.cn.hydra.HangingTypeAircondition
整個(gè)項(xiàng)目結(jié)構(gòu)非常簡單:
這樣,一個(gè)服務(wù)方的簡單實(shí)現(xiàn)就搞定了,用maven打成jar包,之后就可以提供給調(diào)用方使用了。
同理,我們可以再創(chuàng)建一個(gè)立式空調(diào)的項(xiàng)目aircondition-vertical-type
,也只創(chuàng)建一個(gè)服務(wù)類:
public class VerticalTypeAircondition
implements IAircondition{
public String getType() {
return "VerticalType";
}
public void turnOnOff() {
System.out.println("立式空調(diào)開關(guān)");
}
public void adjustTemperature(int i) {
System.out.println("立式空調(diào)調(diào)節(jié)溫度");
}
public void changeModel(int i) {
System.out.println("立式空調(diào)更換模式");
}
}
還是按上面的命名規(guī)則,創(chuàng)建一個(gè)配置文件:
com.cn.hydra.VerticalTypeAircondition
同樣,打成jar包就完事了,至于服務(wù)調(diào)用者如何去發(fā)現(xiàn)和調(diào)用這兩個(gè)服務(wù),下面詳細(xì)再說。
-
JAVA
+關(guān)注
關(guān)注
19文章
2974瀏覽量
105141 -
SPI
+關(guān)注
關(guān)注
17文章
1721瀏覽量
92125 -
代碼
+關(guān)注
關(guān)注
30文章
4825瀏覽量
69046 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14388
發(fā)布評論請先 登錄
相關(guān)推薦
評論