SPI

SPI,全程為Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機制.它通過在ClassPath下META-INF/services文件夾查找文件,自動加載文件中所定義的類.

在這個過程中,可以通過ServiceLoader.load以及ServiceLoader.iterator來獲取文件中定義的實現(xiàn)類
其中,文件名為接口的全限定名,文件中的定義的類是該接口的實現(xiàn)類的全限定名


ServiceLoader部分源碼如下:
ServiceLoader.load() 主要是初始化對象


public final class ServiceLoader<S>
        implements Iterable<S>
{
    //定義查詢文件的目錄
    private static final String PREFIX = "META-INF/services/";

    //要加載的接口
    private final Class<S> service;
    //類加載器
    private final ClassLoader loader;

    private final AccessControlContext acc;
    //緩存已加載的實現(xiàn)類
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    //ServiceLoader 的內(nèi)部類,由它實現(xiàn)加載
    private LazyIterator lookupIterator;

    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {   //初始化ServiceLoader
        return new ServiceLoader<>(service, loader);
    }

    //load 方法,主要做ServiceLoad 及其內(nèi)部類 LazyIterator 初始化(service,loader),
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    public void reload() {
        providers.clear();
        //初始化內(nèi)部類LazyIterator
        lookupIterator = new LazyIterator(service, loader);
    }
    //校驗service是否為空,初始化ServiceLoader
    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();
    }
    
        private class LazyIterator
            implements Iterator<S>
    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;
        //初始化LazyIterator
        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }
  }
}

ServiceLoader.iterator();獲取迭代器,但其實都是在調(diào)用其內(nèi)部類LazyIterator的迭代器

public final class ServiceLoader<S>  implements Iterable<S>{
      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;
               //實際是ServiceLoader內(nèi)部類LazyIterator的hasNext
                return lookupIterator.hasNext();
            }
            
            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
              //實際是ServiceLoader內(nèi)部類LazyIterator的next
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}

LazyIterator的實現(xiàn)

private class LazyIterator implements Iterator<S> {
      private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
            //加載文件中內(nèi)容
                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;
                }
                 //獲取文件中內(nèi)容
                pending = parse(service, configs.nextElement());
            }
            //賦值元素
            nextName = pending.next();
            return true;
        }

        private S nextService() {
             //調(diào)用hasNextService判斷是否還有元素
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
               //根據(jù)實現(xiàn)類權(quán)限定名獲取Class
                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());
                //緩存對象
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

        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);
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
}

開源框架中的實際應(yīng)用:


image.png

其中foreach相當(dāng)于調(diào)用iterator,以及hasNext,next

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末徒役,一起剝皮案震驚了整個濱河市色徘,隨后出現(xiàn)的幾起案子辟拷,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罗侯,死亡現(xiàn)場離奇詭異锅减,居然都是意外死亡煞聪,警方通過查閱死者的電腦和手機鸠澈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來层释,“玉大人婆瓜,你說我怎么就攤上這事」备幔” “怎么了廉白?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乖寒。 經(jīng)常有香客問我猴蹂,道長,這世上最難降的妖魔是什么楣嘁? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任磅轻,我火速辦了婚禮,結(jié)果婚禮上逐虚,老公的妹妹穿的比我還像新娘聋溜。我一直安慰自己,他們只是感情好叭爱,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布撮躁。 她就那樣靜靜地躺著,像睡著了一般买雾。 火紅的嫁衣襯著肌膚如雪把曼。 梳的紋絲不亂的頭發(fā)上杨帽,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機與錄音嗤军,去河邊找鬼注盈。 笑死,一個胖子當(dāng)著我的面吹牛型雳,可吹牛的內(nèi)容都是我干的当凡。 我是一名探鬼主播山害,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼纠俭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浪慌?” 一聲冷哼從身側(cè)響起冤荆,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎权纤,沒想到半個月后钓简,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡汹想,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年外邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片古掏。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡损话,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出槽唾,到底是詐尸還是另有隱情丧枪,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布庞萍,位于F島的核電站拧烦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钝计。R本人自食惡果不足惜恋博,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望私恬。 院中可真熱鬧债沮,春花似錦、人聲如沸践付。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽永高。三九已至隧土,卻和暖如春提针,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背曹傀。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工辐脖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皆愉。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓嗜价,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幕庐。 傳聞我的和親對象是個殘疾皇子久锥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 0 前言 站在一個框架作者的角度來說,定義一個接口异剥,自己默認給出幾個接口的實現(xiàn)類瑟由,同時 允許框架的使用者也能夠自定...
    七寸知架構(gòu)閱讀 16,214評論 3 67
  • Java提供的SPI全名就是Service Provider Interface,下面是一段官方的解釋,,其實就是...
    juexingzhe閱讀 22,293評論 12 74
  • 一冤寿、SPI 簡介: SPI 全稱為 (Service Provider Interface) ,是JDK(1.5...
    zdj凍凍閱讀 1,303評論 0 3
  • 淺談SPI機制 前言 這段時間在研究一個開源框架歹苦,發(fā)現(xiàn)其中有一些以SPI命名的包,經(jīng)過搜索督怜、整理以及思考之后殴瘦,將學(xué)...
    顏洛濱閱讀 20,371評論 0 8
  • 剛剛看了一篇文章蚪腋,哪一個瞬間,你決定嫁給他究流,很多網(wǎng)友們都談到了另一半令他們感動至深的瞬間辣吃,這時,我腦中不斷浮現(xiàn)出他...
    happyhaby閱讀 388評論 0 0