SpringBoot啟動過程的實現(xiàn)
SpringBoot啟動過程分析,首先打開SpringBoot的啟用入口Main類:
@SpringBootApplication
public class ApplicationMain{
public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args);
}
}
可以看到main方法里面只有一行核心啟用類:SpringApplication.run(ApplicationMain.class, args);這個是關鍵,在改行打上斷點,debug模式啟動該main類。點擊下一步進入SpringApplication的源碼對應的run方法:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
初始化SpringApplication
SpringApplication實例化之前會調用構造方法進行初始化:
public SpringApplication(Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.initialize(sources);
}
而SpringApplication構造方法的核心是:this.initialize(sources);初始化方法,SpringApplication通過調用該方法來初始化。
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
1.deduceWebEnvironment方法是用來判斷當前應用的環(huán)境,該方法通過獲取這兩個類來判斷當前環(huán)境是否是web環(huán)境,如果能獲得這兩個類說明是web環(huán)境,否則不是。
javax.servlet.Servlet org.springframework.web.context.ConfigurableWebApplicationContext
2.getSpringFactoriesInstances方法主要用來從spring.factories文件中找出key為ApplicationContextInitializer的類并實例化,然后調用setInitializers方法設置到SpringApplication的initializers屬性中。這個過程就是找出所有的應用程序初始化器。
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//從spring.factories文件中找出key為ApplicationContextInitializer的類
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
當前的初始化器有如下幾個:

3.同理調用getSpringFactoriesInstances從spring.factories文件中找出key為ApplicationListener的類并實例化,然后調用setListeners方法設置到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程序事件監(jiān)聽器。
當前的事件監(jiān)聽器有如下幾個:

4.調用deduceMainApplicationClass方法找出main類,就是這里的ApplicationMain類。
運行SpringApplication
初始化SpringApplication完成之后,調用run方法運行:
public ConfigurableApplicationContext run(String... args) {
//計時器,統(tǒng)計任務的執(zhí)行時間
StopWatch stopWatch = new StopWatch();
//開始執(zhí)行
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
// 獲取SpringApplicationRunListeners啟動事件監(jiān)聽器,這里只有一個EventPublishingRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監(jiān)聽
listeners.starting();
try {
// 構造一個應用程序參數(shù)持有類
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 準備并配置環(huán)境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 打印banner圖形
Banner printedBanner = this.printBanner(environment);
// 創(chuàng)建Spring容器
context = this.createApplicationContext();
new FailureAnalyzers(context);
// 配置Spring容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 容器上下文刷新
this.refreshContext(context);
// 容器創(chuàng)建完成之后調用afterRefresh方法
this.afterRefresh(context, applicationArguments);
// 調用監(jiān)聽器,廣播Spring啟動結束的事件
listeners.finished(context, (Throwable)null);
// 停止計時器
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
SpringApplicationRunListeners
1.獲取啟動事件監(jiān)聽器,可以看看該方法:
SpringApplicationRunListeners listeners = this.getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
同樣的通過調用getSpringFactoriesInstances方法去META-INF/spring.factories文件中拿到SpringApplicationRunListener監(jiān)聽器,當前的SpringApplicationRunListener事件監(jiān)聽器只有一個EventPublishingRunListener廣播事件監(jiān)聽器:

SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日志類。用于SpringApplicationRunListener監(jiān)聽器的批量執(zhí)行。
SpringApplicationRunListener用于監(jiān)聽SpringApplication的run方法的執(zhí)行,它定義了5個步驟:
1.starting:run方法執(zhí)行的時候立馬執(zhí)行,對應的事件類型是ApplicationStartedEvent
2.environmentPrepared:ApplicationContext創(chuàng)建之前并且環(huán)境信息準備好的時候調用,對應的事件類型是ApplicationEnvironmentPreparedEvent
3.contextPrepared:ApplicationContext創(chuàng)建好并且在source加載之前調用一次,沒有具體的對應事件
4.contextLoaded:ApplicationContext創(chuàng)建并加載之后并在refresh之前調用,對應的事件類型是ApplicationPreparedEvent
5.finished:run方法結束之前調用,對應事件的類型是ApplicationReadyEvent或ApplicationFailedEvent
SpringApplicationRunListener目前只有一個實現(xiàn)類EventPublishingRunListener,詳見獲取SpringApplicationRunListeners。它把監(jiān)聽的過程封裝成了SpringApplicationEvent事件并讓內部屬性ApplicationEventMulticaster接口的實現(xiàn)類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。
所以說SpringApplicationRunListener和ApplicationListener之間的關系是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯(lián)系起來的
2.啟動事件監(jiān)聽器
通過listeners.starting()可以啟動事件監(jiān)聽器SpringApplicationRunListener ,SpringApplicationRunListener 是一個啟動事件監(jiān)聽器接口:
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment var1);
void contextPrepared(ConfigurableApplicationContext var1);
void contextLoaded(ConfigurableApplicationContext var1);
void finished(ConfigurableApplicationContext var1, Throwable var2);
}
SpringApplicationRunListener 接口的具體實現(xiàn)就是EventPublishingRunListener類,我們主要來看一下它的startting方法,該方法會封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監(jiān)聽。
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
配置并準備環(huán)境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 創(chuàng)建應用程序的環(huán)境信息。如果是web程序,創(chuàng)建StandardServletEnvironment;否則,創(chuàng)建StandardEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置環(huán)境信息。比如profile,命令行參數(shù)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 廣播出ApplicationEnvironmentPreparedEvent事件給相應的監(jiān)聽器執(zhí)行
listeners.environmentPrepared(environment);
// 環(huán)境信息的校對
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
判斷環(huán)境,如果是web程序,創(chuàng)建StandardServletEnvironment;否則,創(chuàng)建StandardEnvironment。
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
return (ConfigurableEnvironment)(this.webEnvironment ? new StandardServletEnvironment() : new StandardEnvironment());
}
}
創(chuàng)建Spring容器上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 判斷是否是web應用,
// 如果是則創(chuàng)建AnnotationConfigEmbeddedWebApplicationContext,否則創(chuàng)建AnnotationConfigApplicationContext
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
配置Spring容器上下文
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 設置Spring容器上下文的環(huán)境信息
context.setEnvironment(environment);
// Spring容器創(chuàng)建之后做一些額外的事
postProcessApplicationContext(context);
// SpringApplication的初始化器開始工作
applyInitializers(context);
// 遍歷調用SpringApplicationRunListener的contextPrepared方法。目前只是將這個事件廣播器注冊到Spring容器中
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 把應用程序參數(shù)持有類注冊到Spring容器中,并且是一個單例
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 加載sources,sources是main方法所在的類
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 將sources加載到應用上下文中。最終調用的是AnnotatedBeanDefinitionReader.registerBean方法
load(context, sources.toArray(new Object[sources.size()]));
// 廣播出ApplicationPreparedEvent事件給相應的監(jiān)聽器執(zhí)行
// 執(zhí)行EventPublishingRunListener.contextLoaded方法
listeners.contextLoaded(context);
}
Spring容器創(chuàng)建之后回調方法postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 如果SpringApplication設置了實例命名生成器,則注冊到Spring容器中
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
// 如果SpringApplication設置了資源加載器,設置到Spring容器中
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
初始化器開始工作
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍歷每個初始化器,調用對應的initialize方法
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
Spring容器創(chuàng)建完成之后會調用afterRefresh方法
ApplicationRunner、CommandLineRunner類都是在在afterRefresh方法中調用的,也就是說在Spring容器創(chuàng)建之后執(zhí)行的。
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍歷每個初始化器,調用對應的initialize方法
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
參考:https://blog.wangqi.love/articles/Spring/SpringBoot啟動過程.html
到此這篇關于SpringBoot啟動過程的實現(xiàn)的文章就介紹到這了,更多相關SpringBoot啟動過程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java8通過CompletableFuture實現(xiàn)異步回調
這篇文章主要介紹了Java8通過CompletableFuture實現(xiàn)異步回調,CompletableFuture是Java?8?中新增的一個類,它是對Future接口的擴展,下文關于其更多相關詳細介紹需要的小伙伴可以參考一下2022-04-04
SpringBoot異常處理器的使用與添加員工功能實現(xiàn)流程介紹
設計完了登錄與退出功能還只完成了冰山一角,經過測試發(fā)現(xiàn),我們以url的方式來訪問網(wǎng)站時可以直接跳過登陸頁面進入后臺頁面,這樣顯然是不合理的,下面我們通過異常攔截器+boot來做到訪問限制,以及實現(xiàn)新增員工功能,制作全局異常處理器2022-10-10

