Java SPI機制:ServiceLoader實現原理及應用剖析

1、背景

SPI,全稱Service Provider Interfaces,服務提供接口。是Java提供的一套供第三方實現或擴展使用的技術體系。主要經過解耦服務具體實現以及服務使用,使得程序的可擴展性大大加強,甚至可插拔。java

基於服務的註冊與發現機制,服務提供者向系統註冊服務,服務使用者經過查找發現服務,能夠達到服務的提供與使用的分離,甚至完成對服務的管理。android

JDK中,基於SPI的思想,提供了默認具體的實現,ServiceLoader。利用JDK自帶的ServiceLoader,能夠輕鬆實現面向服務的註冊與發現,完成服務提供與使用的解耦編程

完成分離後的服務,使得服務提供方的修改或替換,不會給服務使用方帶來代碼上的修改,基於面向接口的服務約定,提供方和使用方各自直接面向接口編程,而不用關注對方的具體實現。同時,服務使用方使用到服務時,也纔會真正意義上去發現服務,以完成服務的初始化,造成了服務的動態加載緩存

在Java或Android系統實現或項目實踐領域,也有直接基於ServiceLoader的功能實現,或基於ServiceLoader實現基礎上,對其進行的進一步擴展與優化使用。bash


2、ServiceLoader實現原理

先看一下JDK中ServiceLoader的具體實現。 ServiceLoader位於java.util包中,其主體部分,代碼以下:數據結構

public final class ServiceLoader<S> implements Iterable<S> {

    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
    private final Class<S> service;

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;
    
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(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();
    }
    
    // Private inner class implementing fully-lazy provider lookup
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

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

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

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

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

        };
    }
    
    public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
        return new ServiceLoader<>(service, loader);
    }
    
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    
    public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        ClassLoader prev = null;
        while (cl != null) {
            prev = cl;
            cl = cl.getParent();
        }
        return ServiceLoader.load(service, prev);
    }

    public String toString() {
        return "java.util.ServiceLoader[" + service.getName() + "]";
    }
複製代碼

外部使用時,每每經過load(Class<S> service, ClassLoader loader)load(Class<S> service)調用,最後都是在reload方法中建立了LazyIterator對象,LazyIteratorServiceLoader的內部類,實現了Iterator接口,其做用是一個懶加載的迭代器,在hasNextService方法中,完成了對位於META-INF/services/目錄下的配置文件的解析,並在nextService方法中,完成了對具體實現類的實例化。app

META-INF/services/,是ServiceLoader中約定的接口與實現類的關係配置目錄,文件名是接口全限定類名,內容是接口對應的具體實現類,若是有多個實現類,分別將不一樣的實現類都分別做爲每一行去配置。解析過程當中,經過LinkedHashMap<String,S>數據結構的providers,將已經發現了的接口實現類進行了緩存,並對外提供的iterator()方法,方便外部遍歷。框架

Android中使用的是OpenJDK,其ServiceLoader的實現與Java JDK中稍有不一樣,但主體邏輯和實現過程都是一致的。ide

整體上,ServiceLoader的通常實現與使用過程包含了服務接口約定服務實現服務註冊服務發現與使用這四個步驟。工具

以下是一個簡單的Java項目中,ServiceLoader使用示例。

項目結構:
--------------
|____src
| |____main
| | |____resources
| | | |____META-INF
| | | | |____services
| | | | | |____com.corn.javalib.IMyServiceProvider
| | |____java
| | | |____com
| | | | |____corn
| | | | | |____javalib
| | | | | | |____IMyServiceProvider.java
| | | | | | |____TestClass.java
| | | | | | |____MyServiceProviderImpl2.java
| | | | | | |____MyServiceProviderImpl1.java
複製代碼

1,服務接口約定:
IMyServiceProvider定義了服務的接口約定:

package com.corn.javalib;

public interface IMyServiceProvider {

    String getName();

}
複製代碼

2,服務實現:
MyServiceProviderImpl1MyServiceProviderImpl2是具體的接口實現類:

package com.corn.javalib;

public class MyServiceProviderImpl1 implements IMyServiceProvider {

    @Override
    public String getName() {
        return "name:ProviderImpl1";
    }

}
複製代碼
package com.corn.javalib;

public class MyServiceProviderImpl2 implements IMyServiceProvider {

    @Override
    public String getName() {
        return "name:ProviderImpl2";
    }

}
複製代碼

3,服務註冊(實際上向系統登記服務提供者與服務接口之間的映射關係,以便使用方的服務發現):
/META-INF/services/目錄下建立文件com.corn.javalib.IMyServiceProvider,內容爲:

com.corn.javalib.MyServiceProviderImpl1
com.corn.javalib.MyServiceProviderImpl2
複製代碼

4,服務發現與使用:
TestClass.java爲服務使用方。

package com.corn.javalib;

import java.util.Iterator;
import java.util.ServiceLoader;

public class TestClass {

    public static void main(String[] argus){
        ServiceLoader<IMyServiceProvider> serviceLoader = ServiceLoader.load(IMyServiceProvider.class);

        Iterator iterator = serviceLoader.iterator();
        while (iterator.hasNext()){
            IMyServiceProvider item = (IMyServiceProvider)iterator.next();
            System.out.println(item.getName() +  ": " +  item.hashCode());
        }
    }
}

複製代碼

輸出結果爲:

name:ProviderImpl1: 491044090
name:ProviderImpl2: 644117698
複製代碼

3、ServiceLoader使用實例

3.1 註解處理器的發現過程

使用到編譯時註解時,定義註解並在目標元素上標註上註解後,都還須要定義一個具體的註解處理器。註解處理器的做用在於對註解的發現與處理,如實現自定義的註解處理邏輯,生成新的Java文件等。那註解處理器是如何在編譯階段被Javac編譯器發現並調用的呢,這其中的過程實際上用到了ServiceLoader機制。

Javac編譯過程當中,會執行註解處理器發現和調用流程,Javac自己也是用java編寫的,同時去編譯java源碼文件的編譯工具。爲了方便闡述Javac中與註解處理器相關的邏輯,能夠在自定義的註解處理器中故意拋出異常。以查看大概的執行路徑。

Caused by: java.lang.NullPointerException
	at com.corn.apt.AnnotationProcessor.process(AnnotationProcessor.java:42)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
	at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
	at com.sun.tools.javac.main.Main.compile(Main.java:523)
複製代碼

如上,是大概的執行流程。Open JDK中自帶了Javac的源碼,能夠下載下來,主要的Javac源碼部分位於包:com.sun.tools.javac下。對照上述拋出的錯誤信息路徑,梳理下具體的執行流程。
1,javac從com.sun.tools.javac.main.Main.compile開始執行,其中調用了JavaCompilercompile方法,compile方法具體定義以下:

public void compile(List<JavaFileObject> sourceFileObject) throws Throwable {
        compile(sourceFileObject, List.<String>nil(), null);
    }
複製代碼

其內部調用的compile方法,主體部分邏輯以下:

public void compile(List<JavaFileObject> sourceFileObjects,
                        List<String> classnames,
                        Iterable<? extends Processor> processors)
        throws IOException // TODO: temp, from JavacProcessingEnvironment
    {
        if (processors != null && processors.iterator().hasNext())
            explicitAnnotationProcessingRequested = true;
        // as a JavaCompiler can only be used once, throw an exception if
        // it has been used before.
        if (hasBeenUsed)
            throw new AssertionError("attempt to reuse JavaCompiler");
        hasBeenUsed = true;

        start_msec = now();
        try {
            initProcessAnnotations(processors);

            // These method calls must be chained to avoid memory leaks
            delegateCompiler =
                processAnnotations(
                    enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
                    classnames);

            delegateCompiler.compile2();
            delegateCompiler.close();
            elapsed_msec = delegateCompiler.elapsed_msec;
        } catch (Abort ex) {
            if (devVerbose)
                ex.printStackTrace();
        } finally {
            if (procEnvImpl != null)
                procEnvImpl.close();
        }
    }
複製代碼

其中,主要調用了initProcessAnnotations(processors),和processAnnotations(...)方法,且默認狀況下, initProcessAnnotations傳入的processorsnull,是對處理註解進行的初始化,其內部經過new JavacProcessingEnvironment(context, processors)新建了JavacProcessingEnvironment對象,JavacProcessingEnvironment構造器中,經過調用initProcessorIterator(context, processors)開始發現註解處理器。

咱們重點看一下initProcessorIterator具體過程。

private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
    Log   log   = Log.instance(context);
    Iterator<? extends Processor> processorIterator;

    if (options.get("-Xprint") != null) {
        try {
            Processor processor = PrintingProcessor.class.newInstance();
            processorIterator = List.of(processor).iterator();
        } catch (Throwable t) {
            AssertionError assertError =
                new AssertionError("Problem instantiating PrintingProcessor.");
            assertError.initCause(t);
            throw assertError;
        }
    } else if (processors != null) {
        processorIterator = processors.iterator();
    } else {
        String processorNames = options.get("-processor");
        JavaFileManager fileManager = context.get(JavaFileManager.class);
        try {
            // If processorpath is not explicitly set, use the classpath.
            processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
                ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
                : fileManager.getClassLoader(CLASS_PATH);

            /*
             * If the "-processor" option is used, search the appropriate
             * path for the named class.  Otherwise, use a service
             * provider mechanism to create the processor iterator.
             */
            if (processorNames != null) {
                processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
            } else {
                processorIterator = new ServiceIterator(processorClassLoader, log);
            }
        } catch (SecurityException e) {
            /*
             * A security exception will occur if we can't create a classloader. * Ignore the exception if, with hindsight, we didn't need it anyway
             * (i.e. no processor was specified either explicitly, or implicitly,
             * in service configuration file.) Otherwise, we cannot continue.
             */
            processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
        }
    }
    discoveredProcs = new DiscoveredProcessors(processorIterator);
}
複製代碼

傳入的參數processors爲null,默認狀況下,程序邏輯執行到else過程,來到processorIterator = new ServiceIterator(processorClassLoader, log)ServiceIterator是一個內部類,重點看下其具體實現:

/**
 * Use a service loader appropriate for the platform to provide an
 * iterator over annotations processors.  If
 * java.util.ServiceLoader is present use it, otherwise, use
 * sun.misc.Service, otherwise fail if a loader is needed.
 */
private class ServiceIterator implements Iterator<Processor> {
    // The to-be-wrapped iterator.
    private Iterator<?> iterator;
    private Log log;

    ServiceIterator(ClassLoader classLoader, Log log) {
        Class<?> loaderClass;
        String loadMethodName;
        boolean jusl;

        this.log = log;
        try {
            try {
                loaderClass = Class.forName("java.util.ServiceLoader");
                loadMethodName = "load";
                jusl = true;
            } catch (ClassNotFoundException cnfe) {
                try {
                    loaderClass = Class.forName("sun.misc.Service");
                    loadMethodName = "providers";
                    jusl = false;
                } catch (ClassNotFoundException cnfe2) {
                    // Fail softly if a loader is not actually needed.
                    this.iterator = handleServiceLoaderUnavailability("proc.no.service",
                                                                      null);
                    return;
                }
            }

            // java.util.ServiceLoader.load or sun.misc.Service.providers
            Method loadMethod = loaderClass.getMethod(loadMethodName,
                                                      Class.class,
                                                      ClassLoader.class);

            Object result = loadMethod.invoke(null,
                                              Processor.class,
                                              classLoader);

            // For java.util.ServiceLoader, we have to call another
            // method to get the iterator.
            if (jusl) {
                Method m = loaderClass.getMethod("iterator");
                result = m.invoke(result); // serviceLoader.iterator();
            }

            // The result should now be an iterator.
            this.iterator = (Iterator<?>) result;
        } catch (Throwable t) {
            log.error("proc.service.problem");
            throw new Abort(t);
        }
    }

    public boolean hasNext() {
        try {
            return iterator.hasNext();
        } catch (Throwable t) {
            if ("ServiceConfigurationError".
                equals(t.getClass().getSimpleName())) {
                log.error("proc.bad.config.file", t.getLocalizedMessage());
            }
            throw new Abort(t);
        }
    }

    public Processor next() {
        try {
            return (Processor)(iterator.next());
        } catch (Throwable t) {
            if ("ServiceConfigurationError".
                equals(t.getClass().getSimpleName())) {
                log.error("proc.bad.config.file", t.getLocalizedMessage());
            } else {
                log.error("proc.processor.constructor.error", t.getLocalizedMessage());
            }
            throw new Abort(t);
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}
複製代碼

終於,咱們發現,ServiceIterator提供了適合於平臺的服務發現機制,發現註解處理器。其中,優先經過反射的方式,去動態調用了java.util.ServiceLoader對應的load方法,具體是經過語句Object result = loadMethod.invoke(null, Processor.class, classLoader)實現,其中,傳入了接口參數Processor.class以此完成了基於ServiceLoader的服務動態發現過程。

查找到註解處理器後,後續主要就是經過調用對應註解處理器中的方法,如自定義的註解處理器中經常重寫的initprocess方法等,以此實現註解處理器所須要完成的針對註解這塊的邏輯處理。

至此,編譯時的註解處理器的服務發現,其實是經過ServiceLoader去實現的,流程上已經相對清晰。

對應的,咱們也知道,在定義完具體的註解處理器後,須要咱們在對應的/META-INF/services/中去註冊Processor接口與具體的註解處理器實現類之間的關係。固然,這一步操做也能夠聽過Google AutoService去完成。


3.2 基於ServiceLoader實現不一樣組件間的通訊與解耦

Android項目組件化過程當中,不具備依賴關係的組件或模塊間,經常涉及到組件間服務的提供與使用,如A模塊須要調用B模塊的方法等。基於ServiceLoader機制,實際上已經爲咱們提供了一種Android組件化之間的組件解耦與通訊機制。經過將接口約定下沉到公共baseLib模塊,不一樣的模塊內能夠按照實際須要,提供接口的具體實現,其餘模塊直接經過形如ServiceLoader.load(IMyServiceProvider.class)方式,便可獲得具體的服務並調用之。

下面是一個簡單的Android組件化後的項目工程結構示例:

.libBase
|____build.gradle
|____src
| |____main
| | |____res
| | | |____drawable
| | | |____values
| | | | |____strings.xml
| | |____AndroidManifest.xml
| | |____java
| | | |____com
| | | | |____corn
| | | | | |____libbase
| | | | | | |____IMyServiceProvider.java


.LibA
|____libs
|____build.gradle
|____src
| |____main
| | |____res
| | | |____drawable
| | | |____values
| | | | |____strings.xml
| | |____resources
| | | |____META-INF
| | | | |____services
| | | | | |____com.corn.libbase.IMyServiceProvider
| | |____AndroidManifest.xml
| | |____java
| | | |____com
| | | | |____corn
| | | | | |____liba
| | | | | | |____MyServiceProviderImpl2.java
| | | | | | |____MyServiceProviderImpl1.java

.LibB
|____build.gradle
|____src
| |____main
| | |____res
| | | |____drawable
| | | |____values
| | | | |____strings.xml
| | |____resources
| | | |____META-INF
| | | | |____services
| | | | | |____com.corn.libbase.IMyServiceProvider
| | |____AndroidManifest.xml
| | |____java
| | | |____com
| | | | |____corn
| | | | | |____libb
| | | | | | |____MyServiceProviderImpl3.java
複製代碼

經過將服務接口約定定義在libBase模塊,具體的服務實現提供能夠在LibA或LibB等上層模塊,而後分別在META-INF/services/中註冊對應的接口與服務實現之間的關係,使得打包後合併對應的映射關係。

項目中其餘模塊,也能夠直接經過形如以下方式去調用具體的服務方法。

ServiceLoader<IMyServiceProvider> serviceLoader = ServiceLoader.load(IMyServiceProvider.class);
    Iterator iterator = serviceLoader.iterator();
    while (iterator.hasNext()){
        IMyServiceProvider item = (IMyServiceProvider)iterator.next();
        Log.d(TAG, item.getName() +  ": " +  item.hashCode());
    }
複製代碼

若是是在release環境下,還須要針對接口和實現類配置反混淆。不然一旦混淆後,基於接口名或接口實現的配置文件中將不能找到對應的目標類。

以此,經過基於系統ServiceLoader的方式,經過面向接口的編程方式,實現了組件間的服務解耦。


3.3 美團WMRouter中對ServiceLoader的改進與使用

咱們發現,ServiceLoder每次load過程,實際上都重走了整個的ServiceLoder過程,所以,若是直接採用ServiceLoder,每次都須要對具體實現類都重走了查找和經過反射去實例化等過程,且針對同一接口,可能有多個不一樣的服務實現。

在借鑑系統ServiceLoader思想和實現過程的基礎上,美團WMRouter中,對ServiceLoader進行了改進,主要改進點以下:
1,將系統ServiceLoader中的服務註冊從系統原來的/META-INF/services/中定義改爲了WMRouter中封裝好的ServiceLoader中的靜態Map<Class, ServiceLoader> SERVICES屬性和ServiceLoader實例的HashMap<String, ServiceImpl> mMap中。
SERVICES是一個靜態變量,存儲的是接口與對應的ServiceLoader關係映射, mMap做爲ServiceLoader的內部屬性,存儲的是對應ServiceLoader實例中每一個接口實現類的key(每一個key表示每一個不一樣的接口實現)和對應的實現類的關係映射。
2,能夠經過上述的每一個接口的key,讓使用方去具體調用接口的某個具體實現服務。
3,接口實現類,經過反射建立的對象,能夠決定是否存置於SingletonPool單例池中,以方便接口實現類的下次使用,至關於作了一次對象的緩存。

下面具體看下WMRouter中關於ServiceLoader改造部分的源碼實現。

ServiceLoader實現:

package com.sankuai.waimai.router.service;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.sankuai.waimai.router.annotation.RouterProvider;
import com.sankuai.waimai.router.components.RouterComponents;
import com.sankuai.waimai.router.core.Debugger;
import com.sankuai.waimai.router.interfaces.Const;
import com.sankuai.waimai.router.utils.LazyInitHelper;
import com.sankuai.waimai.router.utils.SingletonPool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 經過接口Class獲取實現類
 *
 * @param <I> 接口類型
 */
public class ServiceLoader<I> {

    private static final Map<Class, ServiceLoader> SERVICES = new HashMap<>();

    private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
        @Override
        protected void doInit() {
            try {
                // 反射調用Init類,避免引用的類過多,致使main dex capacity exceeded問題
                Class.forName(Const.SERVICE_LOADER_INIT)
                        .getMethod(Const.INIT_METHOD)
                        .invoke(null);
                Debugger.i("[ServiceLoader] init class invoked");
            } catch (Exception e) {
                Debugger.fatal(e);
            }
        }
    };

    /**
     * @see LazyInitHelper#lazyInit()
     */
    public static void lazyInit() {
        sInitHelper.lazyInit();
    }

    /**
     * 提供給InitClass使用的初始化接口
     *
     * @param interfaceClass 接口類
     * @param implementClass 實現類
     */
    public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
        ServiceLoader loader = SERVICES.get(interfaceClass);
        if (loader == null) {
            loader = new ServiceLoader(interfaceClass);
            SERVICES.put(interfaceClass, loader);
        }
        loader.putImpl(key, implementClass, singleton);
    }

    /**
     * 根據接口獲取 {@link ServiceLoader}
     */
    @SuppressWarnings("unchecked")
    public static <T> ServiceLoader<T> load(Class<T> interfaceClass) {
        sInitHelper.ensureInit();
        if (interfaceClass == null) {
            Debugger.fatal(new NullPointerException("ServiceLoader.load的class參數不該爲空"));
            return EmptyServiceLoader.INSTANCE;
        }
        ServiceLoader service = SERVICES.get(interfaceClass);
        if (service == null) {
            synchronized (SERVICES) {
                service = SERVICES.get(interfaceClass);
                if (service == null) {
                    service = new ServiceLoader(interfaceClass);
                    SERVICES.put(interfaceClass, service);
                }
            }
        }
        return service;
    }

    /**
     * key --> class name
     */
    private HashMap<String, ServiceImpl> mMap = new HashMap<>();

    private final String mInterfaceName;

    private ServiceLoader(Class interfaceClass) {
        if (interfaceClass == null) {
            mInterfaceName = "";
        } else {
            mInterfaceName = interfaceClass.getName();
        }
    }

    private void putImpl(String key, Class implementClass, boolean singleton) {
        if (key != null && implementClass != null) {
            mMap.put(key, new ServiceImpl(key, implementClass, singleton));
        }
    }

    /**
     * 建立指定key的實現類實例,使用 {@link RouterProvider} 方法或無參數構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回null
     */
    public <T extends I> T get(String key) {
        return createInstance(mMap.get(key), null);
    }

    /**
     * 建立指定key的實現類實例,使用Context參數構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回null
     */
    public <T extends I> T get(String key, Context context) {
        return createInstance(mMap.get(key), new ContextFactory(context));
    }

    /**
     * 建立指定key的實現類實例,使用指定的Factory構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回null
     */
    public <T extends I> T get(String key, IFactory factory) {
        return createInstance(mMap.get(key), factory);
    }

    /**
     * 建立全部實現類的實例,使用 {@link RouterProvider} 方法或無參數構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回EmptyList,List中的元素不爲空
     */
    @NonNull
    public <T extends I> List<T> getAll() {
        return getAll((IFactory) null);
    }

    /**
     * 建立全部實現類的實例,使用Context參數構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回EmptyList,List中的元素不爲空
     */
    @NonNull
    public <T extends I> List<T> getAll(Context context) {
        return getAll(new ContextFactory(context));
    }

    /**
     * 建立全部實現類的實例,使用指定Factory構造。對於聲明瞭singleton的實現類,不會重複建立實例。
     *
     * @return 可能返回EmptyList,List中的元素不爲空
     */
    @NonNull
    public <T extends I> List<T> getAll(IFactory factory) {
        Collection<ServiceImpl> services = mMap.values();
        if (services.isEmpty()) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(services.size());
        for (ServiceImpl impl : services) {
            T instance = createInstance(impl, factory);
            if (instance != null) {
                list.add(instance);
            }
        }
        return list;
    }

    /**
     * 獲取指定key的實現類。注意,對於聲明瞭singleton的實現類,獲取Class後仍是能夠建立新的實例。
     *
     * @return 可能返回null
     */
    @SuppressWarnings("unchecked")
    public <T extends I> Class<T> getClass(String key) {
        return (Class<T>) mMap.get(key).getImplementationClazz();
    }

    /**
     * 獲取全部實現類的Class。注意,對於聲明瞭singleton的實現類,獲取Class後仍是能夠建立新的實例。
     *
     * @return 可能返回EmptyList,List中的元素不爲空
     */
    @SuppressWarnings("unchecked")
    @NonNull
    public <T extends I> List<Class<T>> getAllClasses() {
        List<Class<T>> list = new ArrayList<>(mMap.size());
        for (ServiceImpl impl : mMap.values()) {
            Class<T> clazz = (Class<T>) impl.getImplementationClazz();
            if (clazz != null) {
                list.add(clazz);
            }
        }
        return list;
    }

    @SuppressWarnings("unchecked")
    @Nullable
    private <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) {
        if (impl == null) {
            return null;
        }
        Class<T> clazz = (Class<T>) impl.getImplementationClazz();
        if (impl.isSingleton()) {
            try {
                return SingletonPool.get(clazz, factory);
            } catch (Exception e) {
                Debugger.fatal(e);
            }
        } else {
            try {
                if (factory == null) {
                    factory = RouterComponents.getDefaultFactory();
                }
                T t = factory.create(clazz);
                Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t);
                return t;
            } catch (Exception e) {
                Debugger.fatal(e);
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return "ServiceLoader (" + mInterfaceName + ")";
    }

    public static class EmptyServiceLoader extends ServiceLoader {

        public static final ServiceLoader INSTANCE = new EmptyServiceLoader();

        public EmptyServiceLoader() {
            super(null);
        }

        @NonNull
        @Override
        public List<Class> getAllClasses() {
            return Collections.emptyList();
        }

        @NonNull
        @Override
        public List getAll() {
            return Collections.emptyList();
        }

        @NonNull
        @Override
        public List getAll(IFactory factory) {
            return Collections.emptyList();
        }

        @Override
        public String toString() {
            return "EmptyServiceLoader";
        }
    }
}
複製代碼

首先經過對外提供了doInit方法,讓系統經過反射的方式調用ServiceLoaderInit類的init方法,經過調用ServiceLoader.put方法,將接口、接口實現類的key和接口實現類,依次裝載進SERVICESmMap中。以此完成了映射關係的註冊。經過Router類,進一步封裝好對ServiceLoader的調用,以方便外部適用方更方便的去使用,最終經過如Router.getService(ILocationService.class, "keyValue")等方式去調用。

WMRouterServiceLoader自己,其餘的服務查找,服務具體實現類的初始化等相對都比較簡單,下面重點看下服務實現類的實例緩存邏輯。

@Nullable
private <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) {
    if (impl == null) {
        return null;
    }
    Class<T> clazz = (Class<T>) impl.getImplementationClazz();
    if (impl.isSingleton()) {
        try {
            return SingletonPool.get(clazz, factory);
        } catch (Exception e) {
            Debugger.fatal(e);
        }
    } else {
        try {
            if (factory == null) {
                factory = RouterComponents.getDefaultFactory();
            }
            T t = factory.create(clazz);
            Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t);
            return t;
        } catch (Exception e) {
            Debugger.fatal(e);
        }
    }
    return null;
}
複製代碼

createInstance方法中,經過判斷impl.isSingleton(),來決定是否從SingletonPool中讀取緩存的對象實例。SingletonPool是一個單例緩存類,其中經過靜態的CACHE常量,緩存了對應的類class與已經實例化過的對象之間的映射關係,下次再次須要讀取時,直接判斷CACHE中是否已經存在單例對象,有則直接取出,不然建立並存入。

/**
 * 單例緩存
 *
 */
public class SingletonPool {

    private static final Map<Class, Object> CACHE = new HashMap<>();

    @SuppressWarnings("unchecked")
    public static <I, T extends I> T get(Class<I> clazz, IFactory factory) throws Exception {
        if (clazz == null) {
            return null;
        }
        if (factory == null) {
            factory = RouterComponents.getDefaultFactory();
        }
        Object instance = getInstance(clazz, factory);
        Debugger.i("[SingletonPool] get instance of class = %s, result = %s", clazz, instance);
        return (T) instance;
    }

    @NonNull
    private static Object getInstance(@NonNull Class clazz, @NonNull IFactory factory) throws Exception {
        Object t = CACHE.get(clazz);
        if (t != null) {
            return t;
        } else {
            synchronized (CACHE) {
                t = CACHE.get(clazz);
                if (t == null) {
                    Debugger.i("[SingletonPool] >>> create instance: %s", clazz);
                    t = factory.create(clazz);
                    //noinspection ConstantConditions
                    if (t != null) {
                        CACHE.put(clazz, t);
                    }
                }
            }
            return t;
        }
    }
}
複製代碼

SingletonPool的存在,避免了ServiceLoader可能須要的重複實例化過程,但同時,帶來的問題是服務對象的生命週期的延長化,會致使長期佔據內存。由此,做爲框架提供方,特地在服務具體實現類的註解上,加上了一個singleton參數供使用方去決定此服務實現類是否採用單例形式,以決定是否須要緩存。

也就是常見的具體服務類的實現上,註解寫法形式以下:

@RouterService(interfaces = IAccountService.class, key = DemoConstant.SINGLETON, singleton = true)
public class FakeAccountService implements IAccountService {
    ...
    
}
複製代碼

至此,WMRouter中對ServiceLoader的改進部分分析完成。


4、結語

基於服務提供與發現的思想,系統自帶的ServiceLoader以及基於此思想基礎上的演化形式,被普遍的使用到實際的項目中。本質上,經過服務接口約定、服務註冊與服務發現,完成將服務提供方與服務使用方的解耦,大大擴展了系統的可擴展性。服務註冊的本質,是將服務接口與具體服務實現的映射關係註冊到系統或特定實現中。服務發現的過程,本質上是向系統或特定實現去匹配對應的具體實現類,但在寫法上是基於接口的編程方式,由於服務使用方和服務提供方彼此都是透明與未感知的。基於SPI思想的ServiceLoader實現及演化,在項目的組件化,或實現擴展性功能,甚至完成具備可插拔能力的插件化模塊時,每每都被普遍使用到。

相關文章
相關標籤/搜索