一锨天、SPI 簡介:
? ?SPI 全稱為 (Service Provider Interface) ,是JDK(1.5開始)內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制。
二墅诡、SPI場景和產(chǎn)生原因:
我們系統(tǒng)里抽象的各個(gè)模塊成榜,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案意荤,xml解析模塊啊片、jdbc模塊的方案等。面向的對(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需要遵循的規(guī)范:
四葛峻、具體使用
第一步:提供一個(gè)接口和它的若干個(gè)實(shí)現(xiàn):
有一個(gè)接口
package com.xihe.api;
public interface XiheInterface {
? ? public void sayHi();
}
該接口有兩個(gè)實(shí)現(xiàn)
package com.xihe.api;
public class XiheBJ implements XiheInterface {
? ? public void sayHi() {
? ? ? ? System.out.println("xihe in beijing ");
? ? }
}
public class XiheZZ implements XiheInterface {
? ? public void sayHi() {
? ? ? ? System.out.println("xihe in zhengzhou");
? ? }
}
第二步: 在src下創(chuàng)建META-INF/services/目錄中創(chuàng)建一個(gè)名為com.xihe.api.XiheInterface 的文件,文件的內(nèi)容是實(shí)現(xiàn)類的全名。
如果該Service有多個(gè)服務(wù)實(shí)現(xiàn)巴比,則每一行寫一個(gè)服務(wù)實(shí)現(xiàn)术奖,如:
com.xihe.api.XiheZZ
com.xihe.api.XiheBJ
第三步:加載
public class Demo { public static void main(String[] args) { ServiceLoaderserviceLoader = ServiceLoader.load(XiheInterface.class); Iterator it = serviceLoader.iterator();
? ? ? ? while (it!=null && it.hasNext()) {
? ? ? ? ? ? XiheInterface demoService = it.next();
? ? ? ? ? ? System.out.println("class:"+demoService.getClass().getName());
? ? ? ? ? ? demoService.sayHi();
? ? ? ? }
? ? }
}
運(yùn)行結(jié)果:
class:com.xihe.api.XiheZZ
xihe in zhengzhou
class:com.xihe.api.XiheBJ
xihe in beijing
五、源碼分析
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
//ServiceLoader實(shí)現(xiàn)了Iterable接口轻绞,可以遍歷所有的服務(wù)實(shí)現(xiàn)者
public final class ServiceLoader
implements Iterable
{
//查找配置文件的目錄
? ? private static final StringPREFIX ="META-INF/services/";
? ? //表示要被加載的服務(wù)的類或接口
? ? private final Classservice;
? ? //這個(gè)ClassLoader用來定位采记,加載,實(shí)例化服務(wù)提供者
? ? private final ClassLoaderloader;
? ? // 訪問控制上下文
? ? private final AccessControlContextacc;
? ? // 緩存已經(jīng)被實(shí)例化的服務(wù)提供者政勃,按照實(shí)例化的順序存儲(chǔ)
? ? private LinkedHashMapproviders =new LinkedHashMap<>();
? ? // 迭代器
? ? private LazyIteratorlookupIterator;
? ? //重新加載挺庞,就相當(dāng)于重新創(chuàng)建ServiceLoader了,用于新的服務(wù)提供者安裝到正在運(yùn)行的Java虛擬機(jī)中的情況稼病。
? ? public void reload() {
//清空緩存中所有已實(shí)例化的服務(wù)提供者
? ? ? ? providers.clear();
? ? ? ? //新建一個(gè)迭代器,該迭代器會(huì)從頭查找和實(shí)例化服務(wù)提供者
? ? ? ? lookupIterator =new LazyIterator(service, loader);
? ? }
//私有構(gòu)造器
//使用指定的類加載器和服務(wù)創(chuàng)建服務(wù)加載器
//如果沒有指定類加載器掖鱼,使用系統(tǒng)類加載器然走,就是應(yīng)用類加載器。
? ? private ServiceLoader(Class svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
? ? ? ? loader = (cl ==null) ? ClassLoader.getSystemClassLoader() : cl;
? ? ? ? acc = (System.getSecurityManager() !=null) ? AccessController.getContext() :null;
? ? ? ? reload();
? ? }
//解析失敗處理的方法
? ? private static void fail(Class service, String msg, Throwable cause)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() +": " + msg,
? ? ? ? ? ? ? ? cause);
? ? }
private static void fail(Class service, String msg)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() +": " + msg);
? ? }
private static void fail(Class service, URL u, int line, String msg)
throws ServiceConfigurationError
{
fail(service, u +":" + line +": " + msg);
? ? }
//解析服務(wù)提供者配置文件中的一行
//首先去掉注釋校驗(yàn)戏挡,然后保存
//返回下一行行號(hào)
//重復(fù)的配置項(xiàng)和已經(jīng)被實(shí)例化的配置項(xiàng)不會(huì)被保存
? ? private int parseLine(Class service, URL u, BufferedReader r, int lc,
? ? ? ? ? ? ? ? ? ? ? ? ? List names)
throws IOException, ServiceConfigurationError
{
//讀取一行
? ? ? ? String ln = r.readLine();
? ? ? ? if (ln ==null) {
return -1;
? ? ? ? }
//#號(hào)代表注釋行
? ? ? ? int ci = ln.indexOf('#');
? ? ? ? if (ci >=0) ln = ln.substring(0, ci);
? ? ? ? ln = ln.trim();
? ? ? ? int n = ln.length();
? ? ? ? if (n !=0) {
if ((ln.indexOf(' ') >=0) || (ln.indexOf('\t') >=0))
fail(service, u, lc, "Illegal configuration-file syntax");
? ? ? ? ? ? int cp = ln.codePointAt(0);
? ? ? ? ? ? if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
? ? ? ? ? ? for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
? ? ? ? ? ? ? ? if (!Character.isJavaIdentifierPart(cp) && (cp !='.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
? ? ? ? ? ? }
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
? ? ? ? }
return lc +1;
? ? }
//解析配置文件芍瑞,解析指定的url配置文件
//使用parseLine方法進(jìn)行解析,未被實(shí)例化的服務(wù)提供者會(huì)被保存到緩存中去
? ? private Iteratorparse(Class service, URL u)
throws ServiceConfigurationError
{
InputStream in =null;
? ? ? ? BufferedReader r =null;
? ? ? ? ArrayList names =new ArrayList<>();
? ? ? ? try {
in = u.openStream();
? ? ? ? ? ? r =new BufferedReader(new InputStreamReader(in, "utf-8"));
? ? ? ? ? ? int lc =1;
? ? ? ? ? ? while ((lc = parseLine(service, u, r, lc, names)) >=0);
? ? ? ? }
return names.iterator();
? ? }
//服務(wù)提供者查找的迭代器
? ? private class LazyIterator
implements Iterator
{
Classservice;//服務(wù)提供者接口
? ? ? ? ClassLoaderloader;//類加載器
? ? ? ? Enumerationconfigs =null;//保存實(shí)現(xiàn)類的url
? ? ? ? Iteratorpending =null;//保存實(shí)現(xiàn)類的全名
? ? ? ? StringnextName =null;//迭代器中下一個(gè)實(shí)現(xiàn)類的全名
? ? ? ? private LazyIterator(Class service, ClassLoader loader) {
this.service = service;
? ? ? ? ? ? this.loader = loader;
? ? ? ? }
private boolean hasNextService() {
if (nextName !=null) {
return true;
? ? ? ? ? ? }
if (configs ==null) {
try {
String fullName =PREFIX +service.getName();
? ? ? ? ? ? ? ? ? ? if (loader ==null)
configs = ClassLoader.getSystemResources(fullName);
else
? ? ? ? ? ? ? ? ? ? ? ? configs =loader.getResources(fullName);
? ? ? ? ? ? ? ? }
}
while ((pending ==null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
? ? ? ? ? ? ? ? }
pending = parse(service, configs.nextElement());
? ? ? ? ? ? }
nextName =pending.next();
return true;
? ? ? ? }
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
? ? ? ? ? ? String cn =nextName;
? ? ? ? ? ? nextName =null;
? ? ? ? ? ? Class c =null;
? ? ? ? ? ? try {
c = Class.forName(cn, false, loader);
? ? ? ? ? ? }
if (!service.isAssignableFrom(c)) {
fail(service, "Provider " + cn? +" not a subtype");
? ? ? ? ? ? }
try {
S p =service.cast(c.newInstance());
? ? ? ? ? ? ? ? providers.put(cn, p);
? ? ? ? ? ? ? ? return p;
? ? ? ? ? ? }
}
public boolean hasNext() {
if (acc ==null) {
return hasNextService();
? ? ? ? ? ? }else {
PrivilegedAction action =new PrivilegedAction() {
public Booleanrun() {return hasNextService(); }
};
? ? ? ? ? ? ? ? return AccessController.doPrivileged(action, acc);
? ? ? ? ? ? }
}
public S next() {
if (acc ==null) {
return nextService();
? ? ? ? ? ? }else {
PrivilegedAction action =new PrivilegedAction() {
public S run() {return nextService(); }
};
? ? ? ? ? ? ? ? return AccessController.doPrivileged(action, acc);
? ? ? ? ? ? }
}
public void remove() {
throw new UnsupportedOperationException();
? ? ? ? }
}
//獲取迭代器
//返回遍歷服務(wù)提供者的迭代器
//以懶加載的方式加載可用的服務(wù)提供者
//懶加載的實(shí)現(xiàn)是:解析配置文件和實(shí)例化服務(wù)提供者的工作由迭代器本身完成
? ? public Iteratoriterator() {
return new Iterator() {
//按照實(shí)例化順序返回已經(jīng)緩存的服務(wù)提供者實(shí)例
? ? ? ? ? ? Iterator>knownProviders
? ? ? ? ? ? ? ? ? ? =providers.entrySet().iterator();
? ? ? ? ? ? public boolean hasNext() {
if (knownProviders.hasNext())
return true;
? ? ? ? ? ? ? ? return lookupIterator.hasNext();
? ? ? ? ? ? }
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
? ? ? ? ? ? ? ? return lookupIterator.next();
? ? ? ? ? ? }
public void remove() {
throw new UnsupportedOperationException();
? ? ? ? ? ? }
};
? ? }
//為指定的服務(wù)使用指定的類加載器來創(chuàng)建一個(gè)ServiceLoader
? ? public static ServiceLoaderload(Class service,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
? ? }
//使用線程上下文的類加載器來創(chuàng)建ServiceLoader
? ? public static ServiceLoaderload(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
? ? ? ? return ServiceLoader.load(service, cl);
? ? }
//使用擴(kuò)展類加載器為指定的服務(wù)創(chuàng)建ServiceLoader
//只能找到并加載已經(jīng)安裝到當(dāng)前Java虛擬機(jī)中的服務(wù)提供者褐墅,應(yīng)用程序類路徑中的服務(wù)提供者將被忽略
? ? public static ServiceLoaderloadInstalled(Class service) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
? ? ? ? ClassLoader prev =null;
? ? ? ? while (cl !=null) {
prev = cl;
? ? ? ? ? ? cl = cl.getParent();
? ? ? ? }
return ServiceLoader.load(service, prev);
? ? }
public StringtoString() {
return "java.util.ServiceLoader[" +service.getName() +"]";
? ? }
}
ServiceLoader不是實(shí)例化以后拆檬,就去讀取配置文件中的具體實(shí)現(xiàn),并進(jìn)行實(shí)例化妥凳。而是等到使用迭代器去遍歷的時(shí)候竟贯,才會(huì)加載對(duì)應(yīng)的配置文件去解析,調(diào)用hasNext方法的時(shí)候會(huì)去加載配置文件進(jìn)行解析逝钥,調(diào)用next方法的時(shí)候進(jìn)行實(shí)例化并緩存屑那。
所有的配置文件只會(huì)加載一次,服務(wù)提供者也只會(huì)被實(shí)例化一次,重新加載配置文件可使用reload方法持际。
六沃琅、SPI缺點(diǎn)
通過上面的解析,可以發(fā)現(xiàn)蜘欲,我們使用SPI查找具體的實(shí)現(xiàn)的時(shí)候益眉,需要遍歷所有的實(shí)現(xiàn),并實(shí)例化姥份,然后我們?cè)谘h(huán)中才能找到我們需要實(shí)現(xiàn)郭脂。這應(yīng)該也是最大的缺點(diǎn),需要把所有的實(shí)現(xiàn)都實(shí)例化了殿衰,即便我們不需要朱庆,也都給實(shí)例化了。
有關(guān)SPI的東西暫先了解到這里闷祥,有深入的以后再添加娱颊。
參考:
http://www.reibang.com/p/32d3e108f30a
http://cxis.me/2017/04/17/Java%E4%B8%ADSPI%E6%9C%BA%E5%88%B6%E6%B7%B1%E5%85%A5%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/