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


一、背景

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

基於服務的注冊與發現機制,服務提供者向系統注冊服務,服務使用者通過查找發現服務,可以達到服務的提供與使用的分離,甚至完成對服務的管理。

JDK中,基於SPI的思想,提供了默認具體的實現,ServiceLoader。利用JDK自帶的ServiceLoader,可以輕松實現面向服務的注冊與發現,完成服務提供與使用的解耦

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

 

 

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


二、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方法中,完成了對具體實現類的實例化。

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

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

總體上,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
復制代碼

三、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的改進部分分析完成。


四、結語

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


作者:HappyCorn
鏈接:https://juejin.im/post/5d2db85d6fb9a07ea7134408
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM