乐趣区

关于springboot:一java-Spi-与-SpringFactoriesLoader

spring 的 SpringFactoriesLoader 是 spring 框架外部工具类,在 Spring boot 利用启动的过程中,这个类的工作很重要,启动逻辑应用该类从 classpath 上所有 jar 包中找出各自的 META-INF/spring.factories 属性文件,并剖析出其中定义的工厂类。这些工厂类进而被启动逻辑应用,利用于进一步初始化工作。其应用的形式和 java 的 spi 根本相似,咱们能够先学习 java 的 spi 而且进一步学习 SpringFactoriesLoader。

一、java spi

1、什么是 Spi

SPI 全称 Service Provider Interface,是 Java 提供的一套用来被第三方实现或者扩大的接口,它能够用来启用框架扩大和替换组件。SPI 的作用就是为这些被扩大的 API 寻找服务实现。

2、Spi 的利用场景

SPI(Service Provider Interface) 调用方 来制订接口标准,提供给内部来实现,调用方在调用时则 抉择本人须要的内部实现。从应用人员上来说,SPI 被框架扩大人员应用。

3、Spi 的简略 demo

(1)、定义一个接口

public interface Upload {void upload();
}

(2)、接口实现类

package cn..antispam.soa.service;
public class BduUploadImpl implements Upload {
    @Override
 public void upload(){System.out.println("上传到百度云");
 }
}
public class AliUploadImpl implements Upload {
    @Override
 public void upload() {System.out.println("上传到阿里 oss");
 }
}

(3)、测试类

public class spidemo {public static void main(String[] args) {ServiceLoader<Upload> uploads = ServiceLoader.load(Upload.class);
for (Upload upload :uploads){upload.upload();
}
   }
}

(4)输入

上传到阿里 oss
上传到百度云

Process finished with exit code 0

4、Spi 源码剖析

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


    // 扫描目录前缀
    private static final String PREFIX = "META-INF/services/";

    // 被加载的类或接口
    private final Class<S> service;

    // 用于定位、加载和实例化实现方实现的类的类加载器
    private final ClassLoader loader;

    // 上下文对象
    private final AccessControlContext acc;

    // 依照实例化的程序缓存曾经实例化的类
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();

    // 懒加载迭代器
    private java.util.ServiceLoader.LazyIterator lookupIterator;

    // 公有外部类,提供对所有的 service 的类的加载与实例化
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;
        
   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);
    }
      
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        //....
        reload();}
   public void reload() {
       // 清空 providers
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
      
   private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
   }
        //...
        private boolean hasNextService() {if (configs == null) {
                try {
                    // 获取目录下所有的类
                    //"META-INF/services/"+service 的全限定类名
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        // 加载 META-INF/services/"+service 里的所有全限定类名
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {//...}
                //....
            }
        }

        private S nextService() {
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                // 反射加载类
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) { }
            try {
                // 实例化
                S p = service.cast(c.newInstance());
                // 放进缓存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {//..}
            //..
        }
    }
}

下面的代码只贴出了局部要害的实现,上面贴出比拟直观的 spi 加载的次要流程供参考:

退出移动版