1.SPI基本介绍
    SPI,全称即Service Provider Interface,是JDK内置的一种服务发现机制。简单来说,它就是个动态替换实现的机制,根据接口动态加载实现类,目前有不少框架用它来做服务的扩展发现。
    比如我们系统里抽稀的模块,通常需要不同的实现方案,例如常见的日志模块、JDBC模块等等。这样情况下我们一般推荐模块基于接口编程,模块之间禁止硬编码,否则就违反了可插拔原则,如果换一种实现就需要改动代码,这样不太友好。为了实现模块之间的解耦,这就需要一种服务发现机制。Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。
    Java SPI的具体约定为:当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。

    2.SPI在ebatis中的应用
    在ebatis中,SPI主要应用于请求es的地方,可以看到以下代码在执行前后分别有拦截器的处理,拿到结果后对其提取的处理:

    1. try {
    2. INTERCEPTOR.preRequest(args);
    3. R request = createActionRequest(method, args);
    4. //打印request
    5. INTERCEPTOR.postRequest(new DefaultRequestInfo<>(request, args));
    6. ResponseExtractor<?> extractor = method.getResponseExtractor(args).orElseGet(() -> ResponseExtractorFactory.getResponseExtractor(method));
    7. return getExecutor(method).apply(client, request, extractor);
    8. } finally {
    9. ContextHolder.remove();
    10. }

    2.1 Interceptor拦截器的应用
    ebatis支持用户接入服务后自定义拦截器,ebatis提供了Interceptor接口,然后由Interceptors来实现,交由拦截器工厂InterceptorFactory延迟加载各拦截器,这样就组成了拦截器链,如下代码拦截器工厂:

    1. static {
    2. INTERCEPTORS = new LazyInitializer<Interceptor>() {
    3. @Override
    4. protected Interceptor initialize() {
    5. ServiceLoader<Interceptor> interceptorServiceLoader = ServiceLoader.load(Interceptor.class);
    6. List<Interceptor> interceptorList = new ArrayList<>();
    7. for (Interceptor interceptor : interceptorServiceLoader) {
    8. interceptorList.add(interceptor);
    9. }
    10. interceptorList.sort(Comparator.comparingInt(Interceptor::getOrder));
    11. return new Interceptors(interceptorList);
    12. }
    13. };
    14. }

    可以看到ServiceLoader.load加载后,由Interceptor提供的接口排序来决定拦截器的先后执行顺序。
    2.2 ResponseExtractor响应提取器的应用
    代码如下:

    1. ServiceLoader<ResponseExtractorProvider> providers = ServiceLoader.load(ResponseExtractorProvider.class);
    2. for (ResponseExtractorProvider provider : providers) {
    3. if (provider.support(method)) {
    4. return provider.getResponseExtractor(method);
    5. }
    6. }

    在ebatis-spring模块中META-INF/services/已经实现了各个提取器,通过method找到对应的提取器,我们通过引入spring-ebatis集成包便可直接使用,实现方案不对用户暴露。

    3.ServiceLoader源码分析
    我们看到ServiceLoader一个位于sun.misc包,一个位于java.util包,sun包下的源码看不到。通长我们一般用的是位于java.util包下的,就以ServiceLoader.load为例,通过源码看看它里面到底怎么做的。
    3.1 load方法

    1. ClassLoader cl = Thread.currentThread().getContextClassLoader();
    1. 可以看到load内部使用的是上下文类加载器,SPI的接口是Java核心库的一部分,是由启动类加载器来加载的;SPI的实现类是由系统类加载器来加载的。启动类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能委派给系统类加载器,因为它是系统类加载器的祖先类加载器。线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。<br />**3.2 LazyIterator**<br /> 它是一个内部类,查找实现类和创建实现类的过程,都在LazyIterator完成。当我们调用iterator.hasNextiterator.next方法的时候,实际上调用的都是LazyIterator的相应方法。
    1. public Iterator<S> iterator() {
    2. return new Iterator<S>() {
    3. public boolean hasNext() {
    4. return lookupIterator.hasNext();
    5. }
    6. public S next() {
    7. return lookupIterator.next();
    8. }
    9. .......
    10. };
    11. }