全稱爲 Service Provider Interface,是一種服務發現機制。咱們看看簡單的一個例子。
定義一個接口HelloSpiService:緩存
public interface HelloSpiService { void sayHello(); }
實現類HelloSpiServiceImplapp
public class HelloSpiServiceImpl implements HelloSpiService { @Override public void sayHello() { System.out.println("hello"); } }
在資源META-INF/services文件下建立一個文件com.learn.dubbo.spi.HelloSpiService,文件名同接口全路徑,文件內容就是上面的實現類com.learn.dubbo.spi.impl.HelloSpiServiceImpl
,也能夠有多個。
測試代碼:ide
public class Test { public static void main(String[] args) { ServiceLoader<HelloSpiService> load = ServiceLoader.load(HelloSpiService.class); Iterator<HelloSpiService> iterator = load.iterator(); while (iterator.hasNext()) { HelloSpiService helloSpiService = iterator.next(); helloSpiService.sayHello(); } } }
運行結果以下:
Iterator就是文件的實現類內容。源碼分析
public static <S> ServiceLoader<S> load(Class<S> service) { // 獲取當前類加載器 ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) { // 返回一個ServiceLoader return new ServiceLoader<>(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(); } public void reload() { // 清空提供者 providers.clear(); // 初始化lookupIterator,Iterator遍歷就是他作的 lookupIterator = new LazyIterator(service, loader); } private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; }
返回一個迭代器,knownProviders是用來儲存解析過的文件的值,也就是說,上面的load.iterator()
再執行的時候,此時返回的是已解析過的值。若是沒有解析過,那hasNext()
和next()
是由lookupIterator
執行的。測試
public Iterator<S> iterator() { return new Iterator<S>() { Iterator<Map.Entry<String,S>> 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(); } }; }
第一次解析的時候,knownProviders尚未緩存值,因此包括下面的next,都是lookupIterator的方法。
lookupIterator有5個成員變量:ui
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); } } private boolean hasNextService() { // 下一個有值,直接返回true if (nextName != null) { return true; } // 配置文件沒有值,就去讀取 if (configs == null) { try { // 這個PREFIX就是META-INF/services/,咱們建立文件夾的路徑 // fullName接口全路徑 String fullName = PREFIX + service.getName(); // 獲取文件信息 if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } // 獲取文件內容 while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } // 獲取下一個文件內容 nextName = pending.next(); return true; }
主要是讀取文件,把文件內容的實現類的路徑讀取出來。this
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); } } private S nextService() { // 是否有下一個節點 if (!hasNextService()) throw new NoSuchElementException(); // 把下一個節點賦值給cn,而後再重置nextName 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 { // 判斷實例對象,並把值存入providers,後面再load的時候,就是用providers生成的knownProviders S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
主要是獲取實例化對象,並緩存起來。spa