Spring Boot啟動原理:從main方法到內嵌Tomcat的全過程

Spring Boot的啟動過程是一個精心設計的自動化流程,下面我將詳細闡述從main方法開始到內嵌Tomcat啟動的全過程。

1. 入口:main方法

一切始于一個簡單的main方法:

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}

2. SpringApplication初始化

SpringApplication.run()方法內部會創建一個SpringApplication實例:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return new SpringApplication(primarySource).run(args);
}

2.1 構造階段

在SpringApplication構造函數中完成以下關鍵操作:

  • 推斷應用類型:判斷是Servlet應用(Spring MVC)還是Reactive應用(Spring WebFlux)
  • 加載ApplicationContextInitializer:通過META-INF/spring.factories加載
  • 加載ApplicationListener:同樣通過spring.factories機制加載
  • 推斷主配置類:通過堆棧分析找到包含main方法的類

3. 運行階段:run()方法

run()方法是整個啟動過程的核心:

public ConfigurableApplicationContext run(String... args) {// 1. 創建并啟動計時器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. 準備環境ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 5. 打印BannerBanner printedBanner = printBanner(environment);// 6. 創建應用上下文context = createApplicationContext();// 7. 準備應用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 8. 刷新應用上下文(關鍵步驟)refreshContext(context);// 9. 刷新后處理afterRefresh(context, applicationArguments);// 10. 停止計時器并發布啟動完成事件stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);// 11. 執行RunnercallRunners(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. 創建應用上下文

createApplicationContext()方法根據應用類型創建不同的應用上下文:

  • Servlet環境:創建AnnotationConfigServletWebServerApplicationContext
  • Reactive環境:創建AnnotationConfigReactiveWebServerApplicationContext
  • 普通環境:創建AnnotationConfigApplicationContext

對于Web應用,會創建AnnotationConfigServletWebServerApplicationContext,它繼承自ServletWebServerApplicationContext

5. 準備應用上下文

prepareContext()方法完成以下工作:

  • 將環境綁定到上下文
  • 后置處理上下文
  • 應用所有初始化器
  • 發布ContextPrepared事件
  • 注冊主配置類bean定義
  • 發布ContextLoaded事件

6. 刷新應用上下文

refreshContext()最終調用AbstractApplicationContext.refresh(),這是Spring容器的核心刷新流程:

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1. 準備刷新prepareRefresh();// 2. 獲取新的BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3. 準備BeanFactoryprepareBeanFactory(beanFactory);try {// 4. 后置處理BeanFactorypostProcessBeanFactory(beanFactory);// 5. 調用BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// 6. 注冊BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化MessageSourceinitMessageSource();// 8. 初始化事件廣播器initApplicationEventMulticaster();// 9. 初始化特殊bean(由子類實現)onRefresh();// 10. 注冊監聽器registerListeners();// 11. 初始化所有非懶加載單例finishBeanFactoryInitialization(beanFactory);// 12. 完成刷新finishRefresh();}catch (BeansException ex) {// 處理異常...}}
}

7. 內嵌Tomcat啟動的關鍵: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()是內嵌服務器啟動的關鍵:

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {// 1. 獲取WebServer工廠(Tomcat, Jetty或Undertow)ServletWebServerFactory factory = getWebServerFactory();// 2. 創建WebServerthis.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服務器創建過程

以Tomcat為例,TomcatServletWebServerFactory.getWebServer()方法:

public WebServer getWebServer(ServletContextInitializer... initializers) {// 1. 創建Tomcat實例Tomcat tomcat = new Tomcat();// 2. 配置基礎目錄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. 配置Hosttomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());// 5. 準備上下文prepareContext(tomcat.getHost(), initializers);// 6. 創建TomcatWebServer并啟動return getTomcatWebServer(tomcat);
}

9. 啟動Tomcat

TomcatWebServer構造函數中完成Tomcat的啟動:

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {this.tomcat = tomcat;this.autoStart = autoStart;initialize();
}private void initialize() throws WebServerException {// 啟動Tomcatthis.tomcat.start();// 啟動一個守護線程來等待停止命令startDaemonAwaitThread();
}

10. 自動配置的關鍵

整個過程中,自動配置是通過@SpringBootApplication注解中的@EnableAutoConfiguration實現的:

  • invokeBeanFactoryPostProcessors()階段會處理自動配置
  • AutoConfigurationImportSelector會加載META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的配置類
  • 對于Tomcat,會加載ServletWebServerFactoryAutoConfiguration
  • 這個配置類通過@Import引入了EmbeddedTomcat等配置

總結流程

  1. 啟動main方法
  2. 創建SpringApplication實例
  3. 運行run()方法
  4. 準備環境
  5. 創建應用上下文(AnnotationConfigServletWebServerApplicationContext)
  6. 準備上下文(注冊配置類等)
  7. 刷新上下文(核心)
    • 調用onRefresh()
    • 創建內嵌Web服務器(Tomcat)
    • 啟動Tomcat
  8. 發布啟動完成事件
  9. 執行Runner

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89622.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89622.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89622.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

小白學Python,網絡爬蟲篇(1)——requests庫

目錄 一、網絡爬蟲的介紹 1.網絡爬蟲庫 2.robots.txt 規則 二、requests 庫和網頁源代碼 1.requests 庫的安裝 2.網頁源代碼 三、獲取網頁資源 1.get () 函數 &#xff08;1&#xff09;get() 搜索信息 &#xff08;2&#xff09;get() 添加信息 2.返回 Response 對象…

平板可以用來辦公嗎?從文檔處理到創意創作的全面測評

在快節奏的現代職場&#xff0c;一個核心疑問始終縈繞在追求效率的職場人心中&#xff1a;平板電腦&#xff0c;這個輕薄便攜的設備&#xff0c;真的能替代筆記本電腦&#xff0c;成為值得信賴的辦公伙伴嗎&#xff1f; 答案并非簡單的“是”或“否”&#xff0c;而是一個充滿潛…

docker gitlab 備份 恢復 版本升級(16.1.1到18.2.0)

docker 啟動 # 在線 docker pull gitlab/gitlab-ce:latest # 離線 docker save -o gitlab-ce-latest.tar gitlab/gitlab-ce:latest docker load -i gitlab-ce-latest.tardocker run --detach \--publish 8021:80 --publish 8023:22 \ --name gitlab_test \--restart always \-…

web3 區塊鏈技術與用

#53 敲點算法題 瑞吉外賣day4 調整心態 睡眠 及精神 web3 以下是應北京大學肖臻老師《區塊鏈技術與用》公開課的完整教學大綱&#xff0c;綜合課程內容、技術模塊及前沿擴展&#xff0c;分為核心章節與專題拓展兩部分&#xff0c;引用自公開課資料及學員筆記。 &#x1f4…

Redis1:高并發與微服務中的鍵值存儲利器

redis中存儲的數據格式為鍵值對&#xff08;Key,Value&#xff09;在高并發的項目和微服務的項目會頻繁的用到redisNoSQL型數據庫1.初始Redis1.1認識NoSQLSQL&#xff1a;structure query language關系型數據庫結構化&#xff1a;有固定格式要求&#xff08;表關系&#xff0c;…

/字符串/

字符串 個人模板 5. 最長回文子串 93. 復原 IP 地址 43. 字符串相乘 227. 基本計算器 II

我的開發日志:隨機數小程序

文章目錄前言UI設計代碼前言 為什么我要設計這個程序呢&#xff1f;因為我要用&#xff0c;懶得在網上下載了&#xff0c;于是干脆寫了一個。 UI設計 UI是我凹出來的&#xff0c;你們要使用&#xff0c;直接新建一個UI.ui文件&#xff0c;然后把下面的東西輸進去就可以了。 …

《Oracle SQL:使用 RTRIM 和 TO_CHAR 函數格式化數字并移除多余小數點》

select RTRIM(to_char(1222.11123344,fm9999990.9999),.) from dual 這條 SQL 語句主要用于對數字進行格式化處理&#xff0c;并移除格式化結果右側多余的小數點。下面將詳細拆解該語句的執行過程和各部分作用。語句詳細拆解1. to_char(1222.11123344,fm9999990.9999)函數功能&…

「Java案例」方法重裝求不同類型數的立方

利用方法重裝實現不同類型數值的立方計算 立方計算方法的重載實現 編寫一個程序,要求編寫重載方法xxx cube(xxx value)實現對不同類型數值計算立方。 # 源文件保存為“CubeCalculator.java” public class CubeCalculator {public static void main(String[] args) {// 測試…

API 接口開發與接入實踐:自動化采集淘寶商品數據

在電商數據分析、價格監控等場景中&#xff0c;自動化采集淘寶商品數據具有重要價值。本文將詳細介紹如何通過 API 接口開發實現淘寶商品數據的自動化采集&#xff0c;包含完整的技術方案和代碼實現。 一、淘寶 API 接入基礎 1. 接入流程概述 注冊淘寶賬號獲取 ApiKey 和 Ap…

python-pptx 的layout 布局

一、布局基礎概念 在 PowerPoint 中&#xff0c;布局&#xff08;Layout&#xff09; 決定了幻燈片的占位符&#xff08;如標題、內容、圖片等&#xff09;的排列方式。python-pptx 提供了對布局的編程控制。二、默認布局類型及索引 通過 prs.slide_layouts[index] 訪問&#x…

服務器mysql數據的簡單備份腳本

服務器mysql數據的簡單備份腳本 一個小型項目mysql數據庫數據的定時備份 通過crontab定時執行腳本: 0 1 * * * /home/yuyu/mysqlbak.sh備份文件加入時間戳,防止覆蓋支持刪除超過x天的備份數據文件&#xff0c;防止備份數據文件太多 #!/bin/bash# 配置變量 DB_HOST"127.0.…

數據分析:從數據到決策的核心邏輯與實踐指南

在數據驅動決策的時代&#xff0c;“數據分析” 早已不是專業分析師的專屬技能&#xff0c;而是每個職場人都需要掌握的基礎能力。但很多人在面對數據時&#xff0c;常會陷入 “羅列數據卻無結論”“指標好看卻解決不了問題” 的困境。本文將基于數據分析的核心定義、關鍵維度和…

元宇宙與Web3.0:技術特征、關系及挑戰

一、元宇宙的技術特征&#xff08;2025年&#xff09;1. 空間構建技術3D建模與渲染&#xff1a;實時渲染技術&#xff08;如Unity HDRP&#xff09;實現路徑追蹤光追&#xff0c;AI生成模型&#xff08;NVIDIA Get3D&#xff09;3秒生成3D場景。數字孿生技術&#xff1a;城市級…

關于一個引力問題的回答,兼談AI助學作用

關于一個引力問題的回答&#xff0c;兼談AI助學作用今日&#xff0c;一個小朋友問我&#xff0c;他從一本物理科普讀物上看到這樣依據話&#xff1a;地球對人造地球衛星的引力大于太陽對人造地球衛星的引力&#xff0c;但太陽對月亮的引力大于地球對月亮的引力。因書上沒有解釋…

Java使用FastExcel實現模板寫入導出(多級表頭)

依賴配置 (Maven pom.xml)<dependencies><!-- FastExcel 核心庫 --><dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version>1.0.0</version></dependency><!-- Apache POI…

postman接口測試,1個參數有好幾個值的時候如何測試比較簡單快速?

3天精通Postman接口測試手動到自動&#xff0c;全套項目實戰教程&#xff01;&#xff01;當你在 Postman 中測試接口時&#xff0c;如果一個參數有多個需要測試的值&#xff0c;有幾種高效的方法可以實現&#xff1a; 1. 使用 CSV 或 JSON 數據文件進行數據驅動測試 這是最推…

imx6ull UI開發

imx6ull UI開發簡介在imx6ull上開發UI 應用硬件層面內核驅動顯示設備文件描述符設備樹軟件LVGL用戶空間內核QT在imx6ull上開發UI 應用 在 Linux 系統中&#xff0c;應用程序需要通過操作 RGB LCD 的顯存來實現在屏幕上顯示字符、圖像等信息。由于 Linux 采用嚴格的內存管理機制…

虛擬化測試工具Parasoft Virtualize如何為汽車企業提供仿真測試?

在汽車電子研發中&#xff0c;傳統路測曾是驗證ECU&#xff08;電子控制單元&#xff09;、車載通信、OTA升級等功能的可靠手段。然而&#xff0c;隨著智能駕駛和軟件定義汽車&#xff08;SDV&#xff09;的發展&#xff0c;這種依賴物理車輛的測試方式面臨顯著挑戰&#xff1a…

QT之openGL使用(一)

OpenGL簡介 官網&#xff1a;OpenGL - The Industry Standard for High Performance Graphics 中文官網&#xff1a;主頁 - LearnOpenGL CN OpenGL&#xff08;Open Graphics Library&#xff09;是一種跨語言、跨平臺的圖形編程接口&#xff0c;主要用于渲染二維和三維矢量…