Java SPI(Service Provider Interface)

1.什麼是SPI

    SPI全稱Service Provide Interface,是JDK內置的一種服務提供發現機制(如:JDBC)。是一種動態替換髮現機制。例如:有個接口想在運行時才發現具體的實現類,那麼你只需要在程序運行前添加一個實現即可,並把新加的實現描述給JDK即可。實現類系統類加載器AppClassLoader來加載(注:這裏是違反了類加載器雙親委派模式)。

2.SPI規範

3.SPI使用

     1.定義接口

  2.設置實現類

3.設置類加載配置文件

4.使用

添加依賴

類加載

5.SPI缺點

我們使用SPI查找具體的實現的時候,需要遍歷所有的實現,並實例化,然後我們在循環中才能找到我們需要實現。這應該也是最大的缺點,需要把所有的實現都實例化了,即便我們不需要,也都給實例化了。

結果

5.ServiceLoader源碼簡單分析

1.獲取上下文類加載器(App ClassLoader)

/**
*
*獲取上下文類加載器
*/
public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

2.初始化服務提供者查找的迭代器

    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

3.服務提供者查找的迭代器

private class LazyIterator
        implements Iterator<S>
    {

        //服務提供者接口 例如:Hello
        Class<S> service;
        //類加載器
        ClassLoader loader;
        //保存實現類的url
        Enumeration<URL> configs = null;
        //保存實現類的名稱
        Iterator<String> pending = null;
        //下一個實現類的名稱
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }
}

4.實現迭代器的方法 在迭代的時候纔會去獲取具體實現接口的實例

       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());
            }
            //定義下一個實例的名稱
            nextName = pending.next();
            return true;
        }
        //獲取服務
        private S nextService() {
            if (!hasNextService())
                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());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated: " + x,
                     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);
            }
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章