共计 2679 个字符,预计需要花费 7 分钟才能阅读完成。
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 加载的次要流程供参考:
正文完
发表至: springboot
2021-02-04