java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析
1 定義
SPI 的全名為 Service Provider Interface ,用于接口尋找服務(wù)實(shí)現(xiàn)類
實(shí)現(xiàn)方式 >標(biāo)準(zhǔn)制定者制定接口 不同廠商編寫針對(duì)于該接口的實(shí)現(xiàn)類,并在jar的“classpath:META-INF/services/全接口名稱”文件中指定相應(yīng)的實(shí)現(xiàn)類全類名 開發(fā)者直接引入相應(yīng)的jar,就可以實(shí)現(xiàn)為接口自動(dòng)尋找實(shí)現(xiàn)類的功能
2 案例實(shí)現(xiàn)
比如我們經(jīng)??吹降木彺骖怌ache,現(xiàn)在有非常多的緩存框架都會(huì)去實(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í)例化對(duì)象
配置地址
在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)類對(duì)象
通過上述例子,我們知道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í)例化對(duì)象集合 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í)例化的對(duì)象路徑
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()
方法中會(huì)獲取當(dāng)前需要實(shí)例化的類名 nextName ,然后在 next()
方法中具體實(shí)例化
next()
: 先從provider中直接獲取,如果有,返回實(shí)現(xiàn)類對(duì)象實(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對(duì)象 providers.put(cn, p); 放入實(shí)例化對(duì)象集合中 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ī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章

Java通過索引值實(shí)現(xiàn)約瑟夫環(huán)算法

Java實(shí)現(xiàn)按照大小寫字母順序排序的方法

Java使用Lua實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)展和腳本自動(dòng)升級(jí)

Java基礎(chǔ)學(xué)習(xí)之方法的重載知識(shí)總結(jié)

java文件操作工具類實(shí)現(xiàn)復(fù)制文件和文件合并

java web用servlet監(jiān)聽器實(shí)現(xiàn)顯示在線人數(shù)