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加載的主要流程供參考: