JAVA SPI ServiceLoader源碼分析

第一節 參考
第二節 架構
第三節 源碼細節
一.ServiceLoader初始化

ServiceLoader.load(),代碼只有一行,new 一個ServiceLoader對象,

private ServiceLoader(Class<S> svc, ClassLoader cl) {
	//賦值三個成員變量
	//賦值要加載的接口
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    //默認進來是AppClassLoader
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    //默認爲空
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    //清除緩存,初始化接口的迭代器ServiceLoader.LazyIterator對象
    //後面遍歷主要是調用LazyIterator的方法.
    reload();
}

 

二.遍歷接口的實現類
(一).創建迭代器
ServiceLoader.iterator()方法,創建java.util.Iterator對象.Iterator成員變量knownProviders指向ServiceLoader.providers.每次迭代器遍歷時遍歷兩個成員變量,先遍歷ServiceLoader.providers,再遍歷ServiceLoader.lookupIterator.lookupIterator是ServiceLoader.LazyIterator類型.
(二).第一次hasNext.
ServiceLoader.providers爲空Map.進入ServiceLoader.LazyIterator#hasNext()方法.進入LazyIterator.hasNextService().
 

private boolean hasNextService() {
	//第一次進來nextName爲null
	//第一次後就指向下一行實現類,後面調用LazyIterator#nextService()時使用這個nextName,創建實例.
    if (nextName != null) {
        return true;
    }
    //第一遍歷進來configs爲null
    if (configs == null) {
        try {
        	//PREFIX爲"META-INF/services/"路徑 這裏拼接完整的META-INF下interface文件的路徑
            String fullName = PREFIX + service.getName();
            //loader指向AppClassLoader,進入else部分
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
            	//configs是URL類型的Enumeration,加載到這裏面
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
    	//遍歷到最後一行實現類之後,這裏return false,結束遍歷
        if (!configs.hasMoreElements()) {
            return false;
        }
        //讀取spi文件,代碼在後面
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

讀取spi的文件

private Iterator<String> parse(Class<?> service, URL u)
        throws ServiceConfigurationError
{
    InputStream in = null;
    BufferedReader r = null;
    ArrayList<String> names = new ArrayList<>();
    try {
    	//打開spi文件
        in = u.openStream();
        r = new BufferedReader(new InputStreamReader(in, "utf-8"));
        int lc = 1;
        //解析一行實現類,解析到names列表裏面
        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();
}

 

(三).第一次Iterator#next().

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
    	//創建遍歷出來的一行實現類的Class對象,默認使用AppClassLoader
        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());
        //放入到緩存裏面,key是實現類的全路徑名,value是實例對象
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
             x);
    }
    throw new Error();          // This cannot happen
}

 

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