摘要:引語平時倒是聽得很多又是啥別急我們來先看看面向接口編程的調用關系,來了解一下,和的相似和不同之處。理解先來一段官話的介紹全稱為,是內置的一種服務提供發現機制聽了一臉懵逼好的,我們結合圖片來理解一下。然后調用了內部類的方法。
引語
平時API倒是聽得很多?SPI又是啥.別急我們來先看看面向接口編程的調用關系,來了解一下,API和SPI的相似和不同之處。
SPI理解先來一段官話的介紹:SPI 全稱為 (Service Provider Interface) ,是JDK內置的一種服務提供發現機制.(聽了一臉懵逼)好的,我們結合圖片來理解一下。
????簡單的來說分為調用方,接口,服務方.接口就是協議,契約,可以調用方定義,也可以由服務方定義,也就是接口是可以位于調用方的包或者服務方的包.
1.接口的定義和實現都在服務方的時候,僅暴露出接口給調用方使用的時候,我們稱為API;
2.接口的定義在調用方的時候(實現在服務方),我們給它也取個名字--SPI。
應該還比較好理解吧?
SPI在框架中其實有很多廣泛的應用,這里列舉幾個例子:
1.Mysql驅動的選擇driverManager根據配置來確定要使用的驅動;
2.dubbo框架中的擴展機制(dubbo官網鏈接)
看完上面的簡介和SPI在框架中的應用,想必對SPI在讀者的大腦中已經產生了一個雛形,talk is cheap!show me the code.說了這么多,我們具體寫一個簡單的例子來看看效果,驗證一下SPI.
1.首先定義一個接口,忍者服務接口
public interface NinjaService { void performTask(); }
2.接下來寫兩個實現類,ForbearanceServiceImpl(上忍),ShinobuServiceImpl(下忍)
public class ForbearanceServiceImpl implements NinjaService { @Override public void performTask() { System.out.println("上忍在執行A級任務"); } }
public class ShinobuServiceImpl implements NinjaService { @Override public void performTask() { System.out.println("下忍在執行D級任務"); } }
3.接下來我們在main/resources/下創建META-INF/services目錄,并且在services目錄下創建一個com.scott.java.task.spi.NinjaService(忍者服務類的全限定名)的文件.
4.創建一個Client場景類來調用看看結果
很完美的調用了兩個實現類的performTask()方法.
5.最后貼一下目錄結構
附上一波代碼例子的地址,在spi里面,git鏈接;
1.先看下核心類ServiceLoader的定義和屬性
// 繼承了Iterable類 遍歷的時候使用 public final class ServiceLoaderimplements Iterable{ // 這就是為啥需要在META-INF/services/目錄下創建服務類的文件 private static final String PREFIX = "META-INF/services/"; // 被加載的服務 private final Classservice; // 類加載器 private final ClassLoader loader; // 訪問控制類 private final AccessControlContext acc; // 實現類的緩存 根據初始化的順序 也就是在/services/文件中的定義順序來定義的加載順序 private LinkedHashMapproviders = new LinkedHashMap<>(); // 懶加載iterator private LazyIterator lookupIterator;
2.然后從client開始,然后依次debug進去
ServiceLoaderninjaServices = ServiceLoader.load(NinjaService.class);
public staticServiceLoaderload(Classservice) { // 獲取當前的類加載器 也就是AppClassLoader ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
public staticServiceLoaderload(Classservice, ClassLoader loader) { return new ServiceLoader<>(service, loader); }
后面的就省略了,因為這里僅僅就是根據NinjaService初始化的事項,沒有什么很難理解的點.
3.我們在看看具體的調用過程,這里使用的是client對應的class文件,因為增加for(foreach)在java中是個語法糖,實際上編譯后是這樣的內容
public static void main(String[] args) { ServiceLoaderninjaServices = ServiceLoader.load(NinjaService.class); // 這里一下其實就是增加for解糖后的代碼 有興趣可以去了解下java的語法糖 Iterator var2 = ninjaServices.iterator(); while(var2.hasNext()) { NinjaService item = (NinjaService)var2.next(); item.performTask(); } }
4.隨著斷點繼續走,我們進入到var2.hasNext()的方法
public boolean hasNext() { // knownProviders還沒有加載過provider 走下面的分支 if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); }
這里lookupIterator上面ServiceLoader的屬性介紹過,它其實是ServiceLoader中的一個Iterator的內部類。然后調用了內部類Iterator的hasNext()方法。
public boolean hasNext() { // acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; // ServiceLoader初始化沒有設置過securityManager,所以acc是null,進入hasNextService() if (acc == null) { return hasNextService(); } else { PrivilegedActionaction = new PrivilegedAction () { public Boolean run() { return hasNextService(); } }; return AccessController.doPrivileged(action, acc); } }
5.hasNextService()分析
private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { // 這里加載了META-INF/services下的文件 也就是含有兩個實現類全限定名的文件 String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else // 因為loader是不為null 的AppClassLoader 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; }
6.繼續看到parse方法,這里最后返回的是含有兩個全限定類名的Iterator
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); } catch (IOException x) { fail(service, "Error reading configuration file", x); } finally { try { if (r != null) r.close(); if (in != null) in.close(); } catch (IOException y) { fail(service, "Error closing configuration file", y); } } return names.iterator(); }
附帶的說一下parseLine(service, u, r, lc, names),檢查類名是否符合規范,符合的話添加到Iterator中,到這里var2
.hasNext()執行完畢,結果是加載了services下的文件內容
private int parseLine(Class> service, URL u, BufferedReader r, int lc, Listnames) throws IOException, ServiceConfigurationError { String ln = r.readLine(); if (ln == null) { return -1; } 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(" ") >= 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; }
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); } 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()); } // 這里將下一個實現類的名字賦值給了LazyIterator的屬性nextName nextName = pending.next(); return true; }
7.接下來執行的是 NinjaService item = (NinjaService)var2.next()的next(方法),然后繼續debug進去,這里我省略了一些方法的調用,只展示出有用的方法這個是ServiceLoader的內部類LazyIterator的nextService()方法.
private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class> c = null; try { // 這里nextName在上面已經賦值過了 所以反射創建實例 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()); // 將類添加到ServiceLoader的providers屬性中 然后返回 providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
8.到這里子類的實現類返回,分析就結束了.
總結:1.了解了什么是SPI;
2.SPI和API的簡單區別和聯系;
3.學習了怎么使用SPI來擴展服務;
4.分析了ServiceLoader的源碼加載過程,這里扯一句,簡單的就是META-INF/services定義好要實現的接口(文件名)和實現類(文件內容),
ServiceLoader加載的時候沒有實例化實現類,而是在Iterator遍歷的時候去用反射創建了實例.
覺得寫得還行的可以點個贊,關注一波,后面會繼續寫更好的文章~ XD
參考資料:1.http://cr.openjdk.java.net/~m...
2.https://www.cnblogs.com/happy...
3.http://dubbo.apache.org/zh-cn...
4.https://www.cnblogs.com/googl...
5.https://zhuanlan.zhihu.com/p/...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77771.html
摘要:對于這個矛盾的問題,通過自適應拓展機制很好的解決了。自適應拓展機制的實現邏輯比較復雜,首先會為拓展接口生成具有代理功能的代碼。 1、背景 在 Dubbo 中,很多拓展都是通過 SPI 機制進行加載的,比如 Protocol、Cluster、LoadBalance 等。有時,有些拓展并不想在框架啟動階段被加載,而是希望在拓展方法被調用時,根據運行時參數進行加載。這聽起來有些矛盾。拓展未被...
摘要:而編碼器是講應用程序的數據轉化為網絡格式,解碼器則是講網絡格式轉化為應用程序,同時具備這兩種功能的單一組件就叫編解碼器。在中是老的編解碼器接口,而是新的編解碼器接口,并且已經用把適配成了。 遠程通訊——開篇 目標:介紹之后解讀遠程通訊模塊的內容如何編排、介紹dubbo-remoting-api中的包結構設計以及最外層的的源碼解析。 前言 服務治理框架中可以大致分為服務通信和服務管理兩個...
摘要:前言相信做前端的朋友沒有不知道的,都知曉新增了不少新的特性,但是你知道是怎么來的嗎今天就讓閏土來帶大家大話的前世今生。之前可能是自己娛樂為主,大家來旁觀為輔。還有一個比較大的版本就是,它是年正式誕生的。大話前端系列文章較長,未完待續。 showImg(https://segmentfault.com/img/bV4pck?w=1280&h=693); 前言 相信做前端的朋友沒有不知道E...
閱讀 2825·2023-04-25 20:06
閱讀 1446·2021-08-26 14:15
閱讀 2233·2021-08-12 13:27
閱讀 1771·2019-08-30 15:55
閱讀 3469·2019-08-30 13:20
閱讀 2825·2019-08-29 15:12
閱讀 3329·2019-08-29 15:06
閱讀 2858·2019-08-29 14:13