最近在學(xué)習(xí)dubbo源碼,dubbo在功能擴(kuò)展方面洲胖,提供了SPI的擴(kuò)展济榨。初次看到SPI,還是不容易理解绿映,下面記錄SPI的學(xué)習(xí)擒滑。
系統(tǒng)里抽象的各個(gè)模塊,往往有很多不同的實(shí)現(xiàn)方案叉弦,比如日志模塊的方案丐一,xml解析模塊、jdbc模塊的方案等淹冰。面向的對(duì)象的設(shè)計(jì)里库车,我們一般推薦模塊之間基于接口編程,模塊之間不對(duì)實(shí)現(xiàn)類(lèi)進(jìn)行硬編碼樱拴。一旦代碼里涉及具體的實(shí)現(xiàn)類(lèi)柠衍,就違反了可拔插的原則,如果需要替換一種實(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)類(lèi)似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要履怯。
java spi的具體約定如下 :
當(dāng)服務(wù)的提供者回还,提供了服務(wù)接口的一種實(shí)現(xiàn)之后,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件虑乖。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類(lèi)懦趋。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過(guò)該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類(lèi)名疹味,并裝載實(shí)例化仅叫,完成模塊的注入。 基于這樣一個(gè)約定就能很好的找到服務(wù)接口的實(shí)現(xiàn)類(lèi)糙捺,而不需要再代碼里指定诫咱。jdk提供服務(wù)實(shí)現(xiàn)查找的一個(gè)工具類(lèi):java.util.ServiceLoader。
一個(gè)例子說(shuō)明jdk的spi洪灯。假一個(gè)內(nèi)容搜索接口坎缭,搜索的實(shí)現(xiàn)可能是基于文件系統(tǒng)的搜索,也可能是基于數(shù)據(jù)庫(kù)的搜索签钩。
package com.ywsc.fenfenzhong.spi.learn;
import java.util.List;
public interface Search {
public List<String> searchDoc(String keyword);
}
文件搜索實(shí)現(xiàn)
package com.ywsc.fenfenzhong.spi.learn;
import java.util.List;
public class FileSearch implements Search{
@Override
public List<String> searchDoc(String keyword) {
System.out.println("文件搜索 "+keyword);
return null;
}
}
數(shù)據(jù)庫(kù)搜索實(shí)現(xiàn)
package com.ywsc.fenfenzhong.spi.learn;
import java.util.List;
public class DatabaseSearch implements Search{
@Override
public List<String> searchDoc(String keyword) {
System.out.println("數(shù)據(jù)搜索 "+keyword);
return null;
}
}
創(chuàng)建文件 com.ywsc.fenfenzhong.spi.learn.Search
文件內(nèi)容為 com.ywsc.fenfenzhong.spi.learn.FileSearch
目錄結(jié)構(gòu)為
測(cè)試方法
package com.ywsc.fenfenzhong.spi.learn;
import java.util.Iterator;
import java.util.ServiceLoader;
public class TestCase {
public static void main(String[] args) {
ServiceLoader<Search> s = ServiceLoader.load(Search.class);
Iterator<Search> iterator = s.iterator();
while (iterator.hasNext()) {
Search search = iterator.next();
search.searchDoc("hello world");
}
}
}
輸出結(jié)果:文件搜索 hello world