SpringBoot源碼系列:
一文搞懂Spring Boot中java -jar啟動jar包的原理
一文搞懂SpringBoot啟動流程及自動配置
一文搞懂SpringBoot内嵌的Tomcat
一文搞懂SpringApplication對象的建構及spring.factories的加載時機
一文搞懂Spring Boot 事件監聽機制
建構SpringApplication對象源碼:
1、調用啟動類的main()方法,該方法中調用SpringApplication的run方法。
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
2、調用SpringApplication的run()方法的重載方法,在發方法内建構了SpringApplication對象。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
3、建構SpringApplication對象。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader為null
this.resourceLoader = resourceLoader;
//PrimarySources(即啟動類)一定不能為null
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化primarySources屬性
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//從Classpath中推斷Web應用類型。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
建構過程:
接下來我們來詳細研究一下上述過程。在建構SpringApplication對象過程中,此時resourceLoader 為null,primarySources一定不為空且需要初始化為啟動類SpringbootdemoApplication
。從Classpath中推斷出Web應用類型并初始化webApplicationType,通過getSpringFactoriesInstances()擷取Spring工廠執行個體來初始化bootstrapRegistryInitializers,initializers(List<ApplicationContextInitializer<?>>應用程式上下文初始化器清單),
listeners(List<ApplicationListener<?>>
應用監聽器清單)等屬性。最後從堆棧追蹤stackTrace中推導出主類。我們的SpringApplication對象及建構完成。
4、從Classpath推斷Web應用類型,調用的是WebApplicationType類的deduceFromClasspath()方法。如果Classpath中包含DispatcherHandler,則表明目前WebApplicationType為REACTIVE;如果既不包含javax.servlet.Servlet也不包含Spring中的ConfigurableWebApplicationContext,則表明目前WebApplicationType為NONE; 上述兩種都不是則表明目前WebApplicationType是SERVLET
static WebApplicationType deduceFromClasspath() {
// 如果org.springframework.web.reactive.DispatcherHandler的class檔案存在且可以加載
//不存在org.springframework.web.servlet.DispatcherServlet
//不存在org.glassfish.jersey.servlet.ServletContainer
//則傳回REACTIVE表明 該應用程式應作為響應式Web應用程式運作,并應啟動嵌入式servlet Web 伺服器
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//周遊SERVLET 名額類
//如果不存在javax.servlet.Servlet也不存在org.springframework.web.context.ConfigurableWebApplicationContext
//則傳回NONE表明該應用程式不應作為 Web 應用程式運作,也不應啟動嵌入式 Web 伺服器
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//傳回SERVLET表明該應用程式應作為基于servlet的 Web 應用程式運作,并應啟動嵌入式 servlet Web 伺服器。
return WebApplicationType.SERVLET;
}
spring.factories的加載時機
1、在建構SpringApplication中,初始化其屬性bootstrapRegistryInitializers屬性時進行加載 /META-INF/spring.factories。
2、建構SpringApplication對象時,通過調用getSpringFactoriesInstances(Class type)擷取工廠執行個體。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
......
}
3、我們以初始化bootstrapRegistryInitializers為例講解,getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)中首先擷取ClassLoader ,通過SpringFactoriesLoader機制,根據ClassLoader從類路徑jar包中加載META-INF/spring.factories下的所有工廠類及其實作類 。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//擷取ClassLoader
ClassLoader classLoader = getClassLoader();
// 使用SpringFactoriesLoader機制加載出工廠名,并放入Set集合中確定其唯一性。但是在META-INF/spring.factories中并無BootstrapRegistryInitializer是以此處的names的size為0。
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//建立Spring工廠執行個體(但因為上述names的size為0,是以對于BootstrapRegistryInitializer來說它并不會建立SpringFactory執行個體)
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//通過AnnotationAwareOrderComparator對Spring工廠執行個體排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
4、SpringFactoriesLoader中的loadFactoryNames來加載META-INF/spring.factories下的所有工廠類及其實作類
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//擷取目前使用的ClassLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
//為空,則擷取SpringFactoriesLoader的類加載器
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//org.springframework.boot.BootstrapRegistryInitializer
String factoryTypeName = factoryType.getName();
//加載META-INF/spring.factories下的擴充類,沒有值的傳回一個空清單。
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
5、重點關注SpringFactoriesLoader中的loadSpringFactories(ClassLoader classLoader),該方法具體實作了從META-INF/spring.factories下加載擴充類。
//工廠類所在位置,在多個jar檔案中都有。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//檢查緩存中是否有工廠類
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
//緩存中沒有,初始化result
result = new HashMap<>();
try {
//加載META-INF/spring.factories下的一系列元素
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
//疊代周遊
while (urls.hasMoreElements()) {
//spring-boot-版本号.jar檔案中Spring.factories所在的絕對路徑
URL url = urls.nextElement();
//建構UrlResource
UrlResource resource = new UrlResource(url);
//加載該UrlResource中的屬性(即Spring.factories中的鍵值對)
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//父類工廠類型名
String factoryTypeName = ((String) entry.getKey()).trim();
//子類工廠實作類名數組(在Spring.factories中多個用逗号分隔)
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
//将工廠類型名,工廠實作類清單放入名為result的 Map<String, List<String>>中,key為工廠類型名,value為工廠實作類清單。
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
//将result中的所有list都置為包含唯一進制素的不可修改的list
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
//放入緩存。
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
bootstrapRegistryInitializers 的初始化中實作了加載META-INF/spring.factories中工廠擴充類(但是在META-INF/spring.factories并無bootstrapRegistryInitializers ),并将其放入緩存Map<String, List>,其中key為父類工廠名,value為其對應擴充類清單。之後initializers(ApplicationContextInitializer清單)以及listeners(ApplicationListener清單)的初始化都是從該緩存中擷取值。
6、接下裡我們看一下第三步中的createSpringFactoriesInstances()方法。由于bootstrapRegistryInitializers 在META-INF/spring.factories中并不存在。是以我們隻有它傳回的instances是空的即它不會建立SpringFactoriesInstances。但是初始化initializers,listeners時,卻會。我們看一下具體源碼。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
//初始化names.size()大小的list存放建立出來的執行個體。
List<T> instances = new ArrayList<>(names.size());
//周遊傳入的工廠擴充類執行個體名
for (String name : names) {
try {
//通過反射擷取instance的Class對象
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
//instanceClass是一個type類型,向下,否則抛異常IllegalArgumentException
Assert.isAssignable(type, instanceClass);
//擷取instanceClass的構造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//通過BeanUtils的instantiateClass()方法執行個體化類。
T instance = (T) BeanUtils.instantiateClass(constructor, args);
//将執行個體化出來的類放入instances
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}