Spring Boot從main方法到內(nèi)嵌Tomcat的全過程(自動化流程)
Spring Boot的啟動過程是一個精心設計的自動化流程,下面我將詳細闡述從main方法開始到內(nèi)嵌Tomcat啟動的全過程。
1. 入口:main方法
一切始于一個簡單的main方法:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2. SpringApplication初始化
SpringApplication.run()方法內(nèi)部會創(chuàng)建一個SpringApplication實例:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return new SpringApplication(primarySource).run(args);
}
2.1 構(gòu)造階段
在SpringApplication構(gòu)造函數(shù)中完成以下關(guān)鍵操作:
- 推斷應用類型:判斷是Servlet應用(Spring MVC)還是Reactive應用(Spring WebFlux)
- 加載ApplicationContextInitializer:通過META-INF/spring.factories加載
- 加載ApplicationListener:同樣通過spring.factories機制加載
- 推斷主配置類:通過堆棧分析找到包含main方法的類
3. 運行階段:run()方法
run()方法是整個啟動過程的核心:
public ConfigurableApplicationContext run(String... args) {
// 1. 創(chuàng)建并啟動計時器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 初始化應用上下文和異常報告器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 3. 獲取SpringApplicationRunListeners并啟動
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 4. 準備環(huán)境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 5. 打印Banner
Banner printedBanner = printBanner(environment);
// 6. 創(chuàng)建應用上下文
context = createApplicationContext();
// 7. 準備應用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 8. 刷新應用上下文(關(guān)鍵步驟)
refreshContext(context);
// 9. 刷新后處理
afterRefresh(context, applicationArguments);
// 10. 停止計時器并發(fā)布啟動完成事件
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 11. 執(zhí)行Runner
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);
}
return context;
}4. 創(chuàng)建應用上下文
createApplicationContext()方法根據(jù)應用類型創(chuàng)建不同的應用上下文:
- Servlet環(huán)境:創(chuàng)建
AnnotationConfigServletWebServerApplicationContext - Reactive環(huán)境:創(chuàng)建
AnnotationConfigReactiveWebServerApplicationContext - 普通環(huán)境:創(chuàng)建
AnnotationConfigApplicationContext
對于Web應用,會創(chuàng)建AnnotationConfigServletWebServerApplicationContext,它繼承自ServletWebServerApplicationContext。
5. 準備應用上下文
prepareContext()方法完成以下工作:
- 將環(huán)境綁定到上下文
- 后置處理上下文
- 應用所有初始化器
- 發(fā)布ContextPrepared事件
- 注冊主配置類bean定義
- 發(fā)布ContextLoaded事件
6. 刷新應用上下文
refreshContext()最終調(diào)用AbstractApplicationContext.refresh(),這是Spring容器的核心刷新流程:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 準備刷新
prepareRefresh();
// 2. 獲取新的BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 準備BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. 后置處理BeanFactory
postProcessBeanFactory(beanFactory);
// 5. 調(diào)用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注冊BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource
initMessageSource();
// 8. 初始化事件廣播器
initApplicationEventMulticaster();
// 9. 初始化特殊bean(由子類實現(xiàn))
onRefresh();
// 10. 注冊監(jiān)聽器
registerListeners();
// 11. 初始化所有非懶加載單例
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
}
catch (BeansException ex) {
// 處理異常...
}
}
}7. 內(nèi)嵌Tomcat啟動的關(guān)鍵:onRefresh()
對于Servlet Web應用,ServletWebServerApplicationContext重寫了onRefresh()方法:
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}createWebServer()是內(nèi)嵌服務器啟動的關(guān)鍵:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 1. 獲取WebServer工廠(Tomcat, Jetty或Undertow)
ServletWebServerFactory factory = getWebServerFactory();
// 2. 創(chuàng)建WebServer
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}8. Tomcat服務器創(chuàng)建過程
以Tomcat為例,TomcatServletWebServerFactory.getWebServer()方法:
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 創(chuàng)建Tomcat實例
Tomcat tomcat = new Tomcat();
// 2. 配置基礎(chǔ)目錄
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 3. 配置連接器
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
// 4. 配置Host
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
// 5. 準備上下文
prepareContext(tomcat.getHost(), initializers);
// 6. 創(chuàng)建TomcatWebServer并啟動
return getTomcatWebServer(tomcat);
}9. 啟動Tomcat
在TomcatWebServer構(gòu)造函數(shù)中完成Tomcat的啟動:
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
// 啟動Tomcat
this.tomcat.start();
// 啟動一個守護線程來等待停止命令
startDaemonAwaitThread();
}10. 自動配置的關(guān)鍵
整個過程中,自動配置是通過@SpringBootApplication注解中的@EnableAutoConfiguration實現(xiàn)的:
- 在
invokeBeanFactoryPostProcessors()階段會處理自動配置 AutoConfigurationImportSelector會加載META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的配置類- 對于Tomcat,會加載
ServletWebServerFactoryAutoConfiguration - 這個配置類通過
@Import引入了EmbeddedTomcat等配置
總結(jié)流程
- 啟動main方法
- 創(chuàng)建SpringApplication實例
- 運行run()方法
- 準備環(huán)境
- 創(chuàng)建應用上下文(AnnotationConfigServletWebServerApplicationContext)
- 準備上下文(注冊配置類等)
- 刷新上下文(核心)
- 調(diào)用onRefresh()
- 創(chuàng)建內(nèi)嵌Web服務器(Tomcat)
- 啟動Tomcat
- 發(fā)布啟動完成事件
- 執(zhí)行Runner
到此這篇關(guān)于Spring Boot從main方法到內(nèi)嵌Tomcat的全過程(自動化流程)的文章就介紹到這了,更多相關(guān)Spring Boot啟動過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java服務假死之生產(chǎn)事故的排查與優(yōu)化問題
在服務器上通過curl命令調(diào)用一個Java服務的查詢接口,半天沒有任何響應,怎么進行這一現(xiàn)象排查呢,下面小編給大家記一次生產(chǎn)事故的排查與優(yōu)化——Java服務假死問題,感興趣的朋友一起看看吧2022-07-07
如何解決java.net.BindException:地址已在使用問題
當Zookeeper啟動報錯“java.net.BindException:地址已在使用”時,通常是因為指定的端口已被其他進程占用,解決這個問題需要按照以下步驟操作:首先,使用命令如lsof -i:2181找到占用該端口的進程號;其次,使用kill命令終止該進程2024-09-09
Java的Flowable工作流之加簽轉(zhuǎn)簽詳解
這篇文章主要介紹了Java的Flowable工作流之加簽轉(zhuǎn)簽詳解,Flowable是一個開源的工作流引擎,它提供了一套強大的工具和功能,用于設計、執(zhí)行和管理各種類型的工作流程,需要的朋友可以參考下2023-11-11
IDEA下創(chuàng)建SpringBoot+MyBatis+MySql項目實現(xiàn)動態(tài)登錄與注冊功能
這篇文章主要介紹了IDEA下創(chuàng)建SpringBoot+MyBatis+MySql項目實現(xiàn)動態(tài)登錄與注冊功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02
淺談常用Java數(shù)據(jù)庫連接池(小結(jié))
這篇文章主要介紹了淺談常用Java數(shù)據(jù)庫連接池(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-07-07

