系列文章目錄
一、Spring Boot啟動(dòng)的宏觀流程圖
二、Spring Boot啟動(dòng)流程
2.1 初始化new SpringApplication
2.1.1Spring Boot入口
@SpringBootApplication
public class SampleWebJspApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SampleWebJspApplication.class, args);
}
}
2.1.2初始化SpringApplication
準(zhǔn)備階段,在程序運(yùn)行之前初始化一些屬性,用于在后序啟動(dòng)應(yīng)用程序過程中。
//創(chuàng)建 SpringApplication 實(shí)例時(shí),初始化各種重要的屬性,包括應(yīng)用程序的主要來源、Web 應(yīng)用類型、初始化器和監(jiān)聽器等
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//根據(jù)類路徑來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//獲取所有實(shí)現(xiàn)了 ApplicationContextInitializer 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序上下文的初始化器(initializers)。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//獲取所有實(shí)現(xiàn)了 ApplicationListener 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序的監(jiān)聽器(listeners)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//4.設(shè)置應(yīng)用程序運(yùn)行主類
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1.2.1判斷當(dāng)前應(yīng)用程序類型
//根據(jù)類路徑來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//根據(jù)類路徑中存在的特定類來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
static WebApplicationType deduceFromClasspath() {
//檢查類路徑中是否存在 WEBFLUX_INDICATOR_CLASS 并且不存在 WEBMVC_INDICATOR_CLASS 和 JERSEY_INDICATOR_CLASS。
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
//即響應(yīng)式 Web 應(yīng)用程序
return WebApplicationType.REACTIVE;
}
//檢查類路徑中是否存在當(dāng)前遍歷的類名。
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
//表示非 Web 應(yīng)用程序。
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
2.1.2.2設(shè)置應(yīng)用程序的所有初始化器(initializers)
//獲取所有實(shí)現(xiàn)了 ApplicationContextInitializer 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序上下文的初始化器(initializers)。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//通過加載 "spring.factories" 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 從 "spring.factories" 配置文件中加載指定類型的工廠名稱,并將它們放入一個(gè)有序的集合中
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//對(duì)工廠實(shí)例列表進(jìn)行排序。
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
上面這段代碼主要是通過加載 “spring.factories” 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。關(guān)鍵方法是SpringFactoriesLoader.loadFactoryNames()方法。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//只會(huì)返回key為org.springframework.context.ApplicationContextInitializer的value
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
MultiValueMap<String, String> result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
loadFactoryNames()該方法會(huì)從類路徑的 META-INF/spring.factories 讀取相應(yīng)配置文件,讀取配置文件中key為org.springframework.context.ApplicationContextInitializer對(duì)應(yīng)的value。例如,spring-boot這個(gè)jar包它的 META-INF/spring.factories 部分定義為:
獲取到全類名之后,之后會(huì)進(jìn)行實(shí)例化,并返回實(shí)例。如下代碼:
//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
2.1.2.3設(shè)置應(yīng)用程序的所有監(jiān)聽器(listeners)
這只應(yīng)用程序的listeners與設(shè)置初始化器(initializers)的方式相同,會(huì)從類路徑的 META-INF/spring.factories 讀取相應(yīng)配置文件,讀取配置文件中key為org.springframework.context.ApplicationListener對(duì)應(yīng)的value。再根據(jù)拿到的全類名進(jìn)行實(shí)例化,最后完成設(shè)置應(yīng)用程序的所有監(jiān)聽器。
2.1.2.4設(shè)置應(yīng)用程序運(yùn)行主類
//4.設(shè)置應(yīng)用程序運(yùn)行主類
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
到這里SpringApplication實(shí)例的初始化和一些準(zhǔn)備工作就結(jié)束了。
2.2執(zhí)行run方法
宏觀上run方法都做了哪些工作:
public ConfigurableApplicationContext run(String... args) {
//1.這里首先創(chuàng)建了一個(gè) StopWatch 對(duì)象用于計(jì)時(shí)監(jiān)控整個(gè)應(yīng)用程序的啟動(dòng)時(shí)間。
StopWatch stopWatch = new StopWatch();
//啟動(dòng)計(jì)時(shí)監(jiān)控
stopWatch.start();
//2.初始化應(yīng)用上下文
ConfigurableApplicationContext context = null;
//初始化異常報(bào)告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//3.配置 Headless 屬性:Headless 模式用于在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行應(yīng)用程序。
// 這里設(shè)置了 Headless 屬性,以便在沒有 GUI 的環(huán)境中正確啟動(dòng)應(yīng)用程序。
configureHeadlessProperty();
//4.獲取 SpringApplicationRunListeners:這一步是為了獲取所有注冊(cè)的 SpringApplicationRunListener 監(jiān)聽器。
// 這些監(jiān)聽器在應(yīng)用程序的不同生命周期階段會(huì)被通知,從而能夠?qū)?yīng)用程序進(jìn)行擴(kuò)展和自定義處理。
SpringApplicationRunListeners listeners = getRunListeners(args);
//通知監(jiān)聽者,開始啟動(dòng)
// 這里通過調(diào)用 listeners.starting() 來通知所有的 SpringApplicationRunListener 監(jiān)聽器,應(yīng)用程序即將開始啟動(dòng)。
listeners.starting();
try {
//5.獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/*
6.根據(jù)SpringApplicationRunListeners以及參數(shù)來準(zhǔn)備環(huán)境:Spring 環(huán)境包含了應(yīng)用程序的配置信息,
它由多個(gè)屬性源組成,包括默認(rèn)屬性、命令行參數(shù)、系統(tǒng)屬性、配置文件等。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//7.配置忽略 BeanInfo:這是一個(gè)特定的處理,用于在某些環(huán)境中忽略對(duì) JavaBean 的分析。
configureIgnoreBeanInfo(environment);
//8.打印 Banner:Banner 是在應(yīng)用程序啟動(dòng)時(shí)打印的一個(gè) ASCII 藝術(shù)字或自定義標(biāo)志。
Banner printedBanner = printBanner(environment);
//9.創(chuàng)建Spring上下文,其中包含了所有的 Spring Bean 和配置。
context = createApplicationContext();
//10.獲取所有注冊(cè)的 SpringBootExceptionReporter 實(shí)現(xiàn)類的實(shí)例。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//11.Spring上下文前置處理:包括設(shè)置環(huán)境、添加 BeanPostProcessor、注冊(cè) BeanDefinition 等。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//12.Spring上下文刷新:這將觸發(fā) Spring Bean 的實(shí)例化和依賴注入等過程。
refreshContext(context);
//13.spring上下文后置處理:可以在這里執(zhí)行額外的初始化操作。
afterRefresh(context, applicationArguments);
//14.停止計(jì)時(shí)器
stopWatch.stop();
//15.如果設(shè)置了 logStartupInfo,則會(huì)記錄應(yīng)用程序啟動(dòng)信息,包括啟動(dòng)時(shí)間和主類等。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//16.調(diào)用 listeners.running(context) 來通知所有監(jiān)聽者,應(yīng)用程序正在運(yùn)行。
listeners.started(context);
//17.執(zhí)行所有注冊(cè)的 ApplicationRunner 和 CommandLineRunner,這些是在應(yīng)用程序啟動(dòng)完成后自動(dòng)執(zhí)行的任務(wù)。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//18.返回上下文對(duì)象
return context;
}
下面將會(huì)詳細(xì)的介紹各個(gè)環(huán)節(jié):文章來源:http://www.zghlxwxcb.cn/news/detail-612641.html
2.2.1開啟計(jì)時(shí)器
//1.這里首先創(chuàng)建了一個(gè) StopWatch 對(duì)象用于計(jì)時(shí)監(jiān)控整個(gè)應(yīng)用程序的啟動(dòng)時(shí)間。
StopWatch stopWatch = new StopWatch();
//啟動(dòng)計(jì)時(shí)監(jiān)控
stopWatch.start();
//start 方法時(shí),它會(huì)開始記錄給定任務(wù)的運(yùn)行時(shí)間,確保不會(huì)重復(fù)計(jì)時(shí)同一個(gè)任務(wù)。如果你嘗試在計(jì)時(shí)器運(yùn)行時(shí)再次調(diào)用
//start 方法,它將會(huì)拋出異常,防止重復(fù)啟動(dòng)計(jì)時(shí)器。
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
}
2.2.2初始化應(yīng)用上下文和初始化異常報(bào)告
//2.初始化應(yīng)用上下文
ConfigurableApplicationContext context = null;
//初始化異常報(bào)告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
2.2.3設(shè)置Headless屬性
//配置 Headless 屬性:Headless 模式用于在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行應(yīng)用程序。
// 這里設(shè)置了 Headless 屬性,以便在沒有 GUI 的環(huán)境中正確啟動(dòng)應(yīng)用程序。
configureHeadlessProperty();
/*
* @description:這段代碼用于配置 Java AWT 的 Headless 屬性。Headless 模式是在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行 Java 應(yīng)用程序,
* 通常在服務(wù)器上或無界面的操作系統(tǒng)中使用。當(dāng)應(yīng)用程序在 Headless 模式下運(yùn)行時(shí),Java AWT 將不會(huì)初始化圖形界面,以減少資源消耗。
* @author: wangwei
* @date: 2023/7/26 19:39
* @param: []
* @return: void
**/
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
2.2.4獲取所有注冊(cè)的listeners并發(fā)布ApplicationStartingEvent事件。
這里依然是使用了getSpringFactoriesInstances()方法來獲取實(shí)例。
還是從 META-INF/spring.factories 中讀取Key為 org.springframework.boot.SpringApplicationRunListener 的Values,最后根據(jù)全類名進(jìn)行實(shí)例化。文章來源地址http://www.zghlxwxcb.cn/news/detail-612641.html
//獲取 SpringApplicationRunListeners:這一步是為了獲取所有注冊(cè)的 SpringApplicationRunListener 監(jiān)聽器。
// 這些監(jiān)聽器在應(yīng)用程序的不同生命周期階段會(huì)被通知,從而能夠?qū)?yīng)用程序進(jìn)行擴(kuò)展和自定義處理。
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//通過加載 "spring.factories" 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 從 "spring.factories" 配置文件中加載指定類型的工廠名稱,并將它們放入一個(gè)有序的集合中
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//對(duì)工廠實(shí)例列表進(jìn)行排序。
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
最后回到SpringApplication中的run方法中
-------------------------------------------------
//通知監(jiān)聽者,開始啟動(dòng)
// 這里通過調(diào)用 listeners.starting() 來通知所有的 SpringApplicationRunListener 監(jiān)聽器,應(yīng)用程序即將開始啟動(dòng)。
listeners.starting();
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
//事件傳播機(jī)制的實(shí)現(xiàn)。它用于廣播(multicast)一個(gè)應(yīng)用程序事件給所有對(duì)該事件感興趣的監(jiān)聽器,并支持在多線程環(huán)境中異步執(zhí)行監(jiān)聽器的處理邏輯。
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
2.2.5獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)
//獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
//個(gè)命令行參數(shù)解析器的實(shí)現(xiàn),用于將傳入的命令行參數(shù)數(shù)組 args 解析成一個(gè)
//CommandLineArgs 對(duì)象。
//CommandLineArgs 是一個(gè)自定義類,用于存儲(chǔ)解析后的命令行參數(shù)信息。
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
String[] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
String arg = var3[var5];
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionValue = null;
String optionName;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf(61));
optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());
} else {
optionName = optionText;
}
if (optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
} else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
2.2.6讀取環(huán)境配置信息
/*
根據(jù)SpringApplicationRunListeners以及參數(shù)來準(zhǔn)備環(huán)境:Spring 環(huán)境包含了應(yīng)用程序的配置信息,
它由多個(gè)屬性源組成,包括默認(rèn)屬性、命令行參數(shù)、系統(tǒng)屬性、配置文件等。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
/*
* @description:為應(yīng)用程序創(chuàng)建并準(zhǔn)備一個(gè)配置環(huán)境(ConfigurableEnvironment)。它將應(yīng)用程序的命令行參數(shù)、配置屬性等信息設(shè)置到環(huán)境中,
* 并通知相關(guān)的監(jiān)聽器。通過這個(gè)準(zhǔn)備環(huán)境的過程,應(yīng)用程序可以在后續(xù)的啟動(dòng)過程中正確加載和配置所需的屬性和配置信息。
* @author: wangwei
* @date: 2023/7/26 20:14
* @param: [listeners, applicationArguments]
* @return: org.springframework.core.env.ConfigurableEnvironment
**/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//獲取或創(chuàng)建應(yīng)用程序的環(huán)境對(duì)象。環(huán)境是一個(gè)配置容器,用于保存應(yīng)用程序的配置信息、屬性和其他環(huán)境相關(guān)的內(nèi)容。
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置環(huán)境,將應(yīng)用程序的命令行參數(shù)(applicationArguments)設(shè)置到環(huán)境中。
configureEnvironment(environment, applicationArguments.getSourceArgs());
//將配置屬性源(ConfigurationPropertySource)附加到環(huán)境中,用于加載配置屬性。
ConfigurationPropertySources.attach(environment);
//通知監(jiān)聽器,應(yīng)用程序的環(huán)境已準(zhǔn)備好。這是應(yīng)用程序啟動(dòng)過程中通知監(jiān)聽器的一個(gè)生命周期事件。
listeners.environmentPrepared(environment);
//將環(huán)境對(duì)象與當(dāng)前的 Spring 應(yīng)用程序?qū)嵗M(jìn)行綁定,以便可以在應(yīng)用程序中訪問該環(huán)境。
bindToSpringApplication(environment);
//如果當(dāng)前環(huán)境不是自定義環(huán)境,則執(zhí)行下面的邏輯。
if (!this.isCustomEnvironment) {
/*
創(chuàng)建一個(gè) EnvironmentConverter 對(duì)象,并將現(xiàn)有的環(huán)境對(duì)象 environment 作為參數(shù)傳入。
調(diào)用 convertEnvironmentIfNecessary 方法,將當(dāng)前環(huán)境轉(zhuǎn)換成指定類型的環(huán)境。這通常是為了適應(yīng)不同的環(huán)境配置。
*/
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//再次將配置屬性源(ConfigurationPropertySource)附加到環(huán)境中,用于加載配置屬性。
ConfigurationPropertySources.attach(environment);
return environment;
}
2.2.7設(shè)置忽略BeanInfo
//配置忽略 BeanInfo:這是一個(gè)特定的處理,用于在某些環(huán)境中忽略對(duì) JavaBean 的分析。
configureIgnoreBeanInfo(environment);
2.2.8.打印Banner
//打印 Banner:Banner 是在應(yīng)用程序啟動(dòng)時(shí)打印的一個(gè) ASCII 藝術(shù)字或自定義標(biāo)志。
Banner printedBanner = printBanner(environment);
2.2.9創(chuàng)建上下文createApplicationContext
//創(chuàng)建Spring上下文,其中包含了所有的 Spring Bean 和配置。
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
//獲取應(yīng)用程序上下文的類類型。這個(gè)類類型可以在應(yīng)用程序配置中指定,表示要使用的自定義上下文類。
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
//如果 this.webApplicationType 為 SERVLET,則使用默認(rèn)的 Servlet Web 上下文類
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
//如果 this.webApplicationType 為 SERVLET,則使用默認(rèn)的 Servlet Web 上下文類
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
//對(duì)于其他情況,默認(rèn)使用普通的上下文類類型 DEFAULT_CONTEXT_CLASS。
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
//實(shí)例化 contextClass 對(duì)象,得到一個(gè)應(yīng)用程序上下文的實(shí)例。
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
2.2.10.獲取所有異常報(bào)告器
//獲取所有注冊(cè)的 SpringBootExceptionReporter 實(shí)現(xiàn)類的實(shí)例。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加載指定 type 類型的工廠類名稱。
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根據(jù)工廠類名稱創(chuàng)建實(shí)例對(duì)象。
//這個(gè)方法將通過反射實(shí)例化工廠類,并調(diào)用其對(duì)應(yīng)的靜態(tài)工廠方法或構(gòu)造函數(shù)。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.2.11準(zhǔn)備上下文prepareContext
/*
* @description:準(zhǔn)備應(yīng)用程序的上下文。它將配置環(huán)境設(shè)置到上下文中,調(diào)用初始化器進(jìn)行上下文初始化,注冊(cè)一些特定的單例 Bean,加載應(yīng)用程序的資源,
* 并通知監(jiān)聽器應(yīng)用程序的上下文已準(zhǔn)備好和已加載完成。
* 這個(gè)過程是 Spring Boot 應(yīng)用程序啟動(dòng)過程中的重要一環(huán),確保應(yīng)用程序上下文正確配置和初始化。
* @author: wangwei
* @date: 2023/7/26 20:55
* @param:
* @return:
**/
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//將應(yīng)用程序的配置環(huán)境 environment 設(shè)置到上下文 context 中。
context.setEnvironment(environment);
//對(duì)應(yīng)用程序的上下文進(jìn)行后置處理。這是 Spring 的擴(kuò)展點(diǎn),可以在應(yīng)用程序上下文創(chuàng)建后,對(duì)其進(jìn)行額外的定制。
postProcessApplicationContext(context);
//調(diào)用應(yīng)用程序的初始化器(ApplicationContextInitializer)對(duì)上下文進(jìn)行初始化。初始化器是一種在 Spring Boot 應(yīng)用程序啟動(dòng)時(shí)自動(dòng)執(zhí)行的擴(kuò)展機(jī)制。
applyInitializers(context);
//通知監(jiān)聽器,應(yīng)用程序的上下文已準(zhǔn)備好。這是應(yīng)用程序啟動(dòng)過程中通知監(jiān)聽器的一個(gè)生命周期事件。
listeners.contextPrepared(context);
if (this.logStartupInfo) {
//如果應(yīng)用程序的上下文沒有父級(jí)上下文,則記錄啟動(dòng)信息。
logStartupInfo(context.getParent() == null);
//記錄應(yīng)用程序的啟動(dòng)配置信息和配置文件。
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//向上下文注冊(cè)特定的單例 Bean:
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注冊(cè) applicationArguments,應(yīng)用程序的命令行參數(shù)對(duì)象。
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//注冊(cè) printedBanner,打印的橫幅對(duì)象。
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
//設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
load(context, sources.toArray(new Object[0]));
//設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
listeners.contextLoaded(context);
}
2.2.12.刷新上下文refreshContext
//Spring上下文刷新:這將觸發(fā) Spring Bean 的實(shí)例化和依賴注入等過程。
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
//Spring 應(yīng)用程序上下文刷新的核心方法,負(fù)責(zé)進(jìn)行初始化、配置和處理各種 Bean,使得應(yīng)用程序上下文準(zhǔn)備好運(yùn)行,可以在后續(xù)的運(yùn)行中使用和管理 Bean。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//準(zhǔn)備應(yīng)用程序上下文進(jìn)行刷新操作。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//準(zhǔn)備應(yīng)用程序上下文進(jìn)行刷新操作。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//準(zhǔn)備 Bean 工廠供在上下文中使用
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//允許上下文子類后處理 Bean 工廠,做一些額外的配置。
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//調(diào)用注冊(cè)在上下文中的 Bean 工廠后置處理器,這些后置處理器用于在 Bean 工廠實(shí)例化 Bean 之前修改、擴(kuò)展或配置 Bean 工廠。
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注冊(cè) Bean 后置處理器(BeanPostProcessor)到 Bean 工廠中。Bean 后置處理器在 Bean 的實(shí)例化、初始化以及銷毀的過程中,可以對(duì) Bean 進(jìn)行攔截和處理。
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化消息資源,用于國(guó)際化和本地化消息處理。
initMessageSource();
// Initialize event multicaster for this context.
//初始化應(yīng)用程序事件廣播器,用于發(fā)布和處理應(yīng)用程序事件。
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//在特定上下文子類中,進(jìn)行其他特殊 Bean 的初始化和配置。
onRefresh();
// Check for listener beans and register them.
//檢查并注冊(cè)事件監(jiān)聽器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//實(shí)例化所有剩余的(非懶加載)單例 Bean。
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成刷新操作,發(fā)布 Spring 容器刷新事件。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//清空 Spring 核心中的常見的元數(shù)據(jù)緩存,以便在運(yùn)行時(shí)不再需要單例 Bean 的元數(shù)據(jù)信息。
resetCommonCaches();
}
}
}
2.2.13上下文后置處理
//在默認(rèn)情況下不執(zhí)行任何操作,留給子類去擴(kuò)展和實(shí)現(xiàn)。
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
2.2.14停止計(jì)時(shí)器
//停止
StopWatch 的計(jì)時(shí),并記錄上一次計(jì)時(shí)任務(wù)的執(zhí)行時(shí)間。
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
} else {
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
2.2.15.打印應(yīng)用程序啟動(dòng)信息
//如果設(shè)置了 logStartupInfo,則會(huì)記錄應(yīng)用程序啟動(dòng)信息,包括啟動(dòng)時(shí)間和主類等。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
2.2.16 通知監(jiān)聽器,應(yīng)用程序正在運(yùn)行
//是通知所有注冊(cè)的 SpringApplicationRunListener 實(shí)現(xiàn)類,應(yīng)用程序上下文已經(jīng)啟動(dòng)完成。
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
2.2.17執(zhí)行所有Runner
//它在應(yīng)用程序上下文創(chuàng)建完成后,調(diào)用所有注冊(cè)的 ApplicationRunner 和 CommandLineRunner bean 的 run() 方法。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
2.2.18.返回上下文對(duì)象context
return context;
到了這里,關(guān)于Spring Boot——Spring Boot啟動(dòng)原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!