java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析
1 定義
SPI 的全名為 Service Provider Interface ,用于接口尋找服務(wù)實(shí)現(xiàn)類
實(shí)現(xiàn)方式 >標(biāo)準(zhǔn)制定者制定接口 不同廠商編寫針對于該接口的實(shí)現(xiàn)類,并在jar的“classpath:META-INF/services/全接口名稱”文件中指定相應(yīng)的實(shí)現(xiàn)類全類名 開發(fā)者直接引入相應(yīng)的jar,就可以實(shí)現(xiàn)為接口自動尋找實(shí)現(xiàn)類的功能
2 案例實(shí)現(xiàn)
比如我們經(jīng)??吹降木彺骖怌ache,現(xiàn)在有非常多的緩存框架都會去實(shí)現(xiàn)這個(gè)接口
標(biāo)準(zhǔn)接口
public interface Cache {
String getName();
<T> T get(Object key, Class<T> type);
void put(Object key, Object value);
void evict(Object key);
void clear();
}
廠商的具體接口實(shí)現(xiàn)
public class ConcurrentMapCache implements Cache {
private final String name;
private final ConcurrentMap<Object, Object> store;
public ConcurrentMapCache() {
this("defaultMapCache");
}
public ConcurrentMapCache(String name) {
this(name, new ConcurrentHashMap<>(256), true);
}
public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {
this.name = name;
this.store = store;
}
@Override
public final String getName() {
return this.name;
}
@Override
public <T> T get(Object key, Class<T> type) {
Object value = this.store.get(key);
if (value != null && type != null && !type.isInstance(value)) {
throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
}
return (T) value;
}
@Override
public void put(Object key, Object value) {
this.store.putIfAbsent(key, value);
}
@Override
public void evict(Object key) {
this.store.remove(key);
}
@Override
public void clear() {
this.store.clear();
}
}
注意:一定要有默認(rèn)無參構(gòu)造器,否則之后無法通過SPI機(jī)制實(shí)例化對象
配置地址

在resouce下的META-INF\services文件下的spi.Cache文件內(nèi)容是服務(wù)類的全限命名:spi.ConcurrentMapCache
打包jar并引入到項(xiàng)目
測試
public class CacheSpiTest {
public static void main(String[] args) {
ServiceLoader<Cache> serviceLoader = ServiceLoader.load(Cache.class);
Iterator<Cache> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
Cache cache = iterator.next();
System.out.println(cache.getName());
cache.put("user", "nana");
System.out.println(cache.get("user", String.class));
}
}
}
打印結(jié)果:
defaultMapCache
nana
說明獲取到了定制接口的實(shí)現(xiàn)類對象
通過上述例子,我們知道ServiceLoader是用于通過接口獲取接口實(shí)現(xiàn)類的工具
3 SPI機(jī)制源碼分析
3.1 load加載過程
ServiceLoader成員變量
//SPI約定獲取擴(kuò)展接口路徑的文件 private static final String PREFIX = "META-INF/services/"; //基礎(chǔ)約定接口 private final Class<S> service; private final ClassLoader loader; //權(quán)限控制上下文 private final AccessControlContext acc; //廠商接口實(shí)現(xiàn)類的實(shí)例化對象集合 private LinkedHashMap<String,S> providers = new LinkedHashMap<>();//以初始化的順序緩存<接口全名稱, 實(shí)現(xiàn)類實(shí)例> //懶加載迭代器 private LazyIterator lookupIterator
load()初始化
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
private ServiceLoader(Class<S> 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();
}
load() 方法并沒有實(shí)例化具體實(shí)現(xiàn)類,而是加載需要實(shí)例化的對象路徑
3.2 實(shí)例化過程
Class<S> service;//通用接口
ClassLoader loader;//類加載器
Enumeration<URL> configs = null;//廠商接口文件URL的集合
Iterator<String> pending = null;//接口具體實(shí)現(xiàn)的路徑類名列表
public boolean hasNext() {
if (acc == null) {//訪問控制上下文是否為空
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
hasNext() : 先從provider中查找,如果有,返回true;如果沒有,通過LazyIterator 來進(jìn)行查找. 在 hasNext() 方法中會獲取當(dāng)前需要實(shí)例化的類名 nextName ,然后在 next() 方法中具體實(shí)例化
next(): 先從provider中直接獲取,如果有,返回實(shí)現(xiàn)類對象實(shí)例;如果沒有,通過LazyIterator 中 nextService() 來進(jìn)行獲取
private S nextService() {
if (!hasNextService()) //獲取nextName 需要加載的類名
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance()); //初始化類并類型轉(zhuǎn)換成Cache對象
providers.put(cn, p); 放入實(shí)例化對象集合中
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated", x);
}
throw new Error(); // This cannot happen
}
上述代碼主要是延遲實(shí)例化類,然后緩存進(jìn)集合,方便下次直接使用
以上就是java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析的詳細(xì)內(nèi)容,更多關(guān)于java框架基礎(chǔ)SPI機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決@NonNull @org.jetbrains.annotations.NotNull報(bào)紅的問題
這篇文章主要介紹了解決@NonNull @org.jetbrains.annotations.NotNull報(bào)紅的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(48)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08
Java 7菱形語法與泛型構(gòu)造器實(shí)例分析
這篇文章主要介紹了Java 7菱形語法與泛型構(gòu)造器,結(jié)合實(shí)例形式分析了Java菱形語法與泛型構(gòu)造器相關(guān)原理與使用技巧,需要的朋友可以參考下2019-07-07
Springboot實(shí)現(xiàn)全局自定義異常的方法詳解
這篇文章主要介紹了Springboot實(shí)現(xiàn)全局自定義異常的方法詳解,SpringBoot的項(xiàng)目已經(jīng)對有一定的異常處理了,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進(jìn)行統(tǒng)一的捕獲并處理,需要的朋友可以參考下2023-11-11
使用Mybatis遇到的there is no getter異常
這篇文章主要介紹了使用Mybatis遇到的there is no getter異常,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
關(guān)于dubbo的RPC和RESTful性能及對比
這篇文章主要介紹了關(guān)于dubbo的RPC和RESTful性能及對比,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12

