SpringBoot通過main方法啟動web項目實踐
Spring Boot 通過 main
方法啟動 Web 項目的過程涉及多個核心組件和自動化機(jī)制,下面從源碼角度詳細(xì)拆解:
1. 啟動入口:SpringApplication.run()
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
SpringApplication.run()
是啟動的核心入口,它主要完成以下工作:
2. SpringApplication初始化
// SpringApplication 構(gòu)造函數(shù)核心邏輯 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 1. 設(shè)置資源加載器 this.resourceLoader = resourceLoader; // 2. 校驗并保存主配置類(即 @SpringBootApplication 標(biāo)注的類) this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 3. 推斷應(yīng)用類型(REACTIVE、SERVLET、NONE) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 4. 加載并實例化 ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 5. 加載并實例化 ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 6. 推斷 main 方法所在類 this.mainApplicationClass = deduceMainApplicationClass(); }
關(guān)鍵步驟:
- 應(yīng)用類型推斷:通過檢查類路徑中是否存在
org.springframework.web.reactive.DispatcherHandler
(REACTIVE)或javax.servlet.Servlet
(SERVLET)來確定應(yīng)用類型。 - 初始化器(Initializer):從
META-INF/spring.factories
加載ApplicationContextInitializer
,用于在ApplicationContext
刷新前自定義配置。 - 監(jiān)聽器(Listener):加載
ApplicationListener
,監(jiān)聽啟動過程中的事件(如ApplicationStartingEvent
)。
3. run()方法核心流程
public ConfigurableApplicationContext run(String... args) { // 1. 計時和發(fā)布啟動事件 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 2. 獲取并啟動監(jiān)聽器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { // 3. 構(gòu)建應(yīng)用參數(shù)和環(huán)境配置 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 4. 創(chuàng)建并配置 ApplicationContext context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 5. 準(zhǔn)備上下文(加載 Bean 定義) prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 6. 刷新上下文(核心啟動邏輯) refreshContext(context); // 7. 刷新后的回調(diào)處理 afterRefresh(context, applicationArguments); // 8. 發(fā)布應(yīng)用就緒事件 listeners.started(context); // 9. 執(zhí)行 Runner(如 CommandLineRunner) callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 10. 發(fā)布應(yīng)用運(yùn)行中事件 listeners.running(context); return context; }
4. 嵌入式 Web 服務(wù)器啟動關(guān)鍵點(diǎn)
4.1refreshContext()方法觸發(fā)服務(wù)器啟動
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ConfigurableApplicationContext context) { // 調(diào)用 AbstractApplicationContext 的 refresh() 方法 context.refresh(); }
4.2ServletWebServerApplicationContext的核心作用
對于 Web 應(yīng)用,ApplicationContext
實際類型為 AnnotationConfigServletWebServerApplicationContext
,它繼承自 ServletWebServerApplicationContext
,后者在 refresh()
過程中會:
// ServletWebServerApplicationContext 核心方法 @Override protected void onRefresh() { super.onRefresh(); try { // 創(chuàng)建并啟動嵌入式 Web 服務(wù)器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { // 1. 獲取 ServletWebServerFactory(如 TomcatServletWebServerFactory) ServletWebServerFactory factory = getWebServerFactory(); // 2. 創(chuàng)建并配置 Web 服務(wù)器 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(); }
4.3ServletWebServerFactory實例化服務(wù)器
以 Tomcat 為例,TomcatServletWebServerFactory
的 getWebServer()
方法會:
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { // 1. 創(chuàng)建 Tomcat 實例 Tomcat tomcat = new Tomcat(); // 2. 配置服務(wù)器基本參數(shù)(端口、上下文路徑等) File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); // 3. 配置 ServletContextInitializer(如 DispatcherServlet) prepareContext(tomcat.getHost(), initializers); // 4. 啟動服務(wù)器 return getTomcatWebServer(tomcat); }
5. Spring MVC 組件自動配置
通過 WebMvcAutoConfiguration
自動配置核心組件:
DispatcherServlet
:作為前端控制器,處理所有 HTTP 請求。HandlerMapping
:映射 URL 到具體的 Controller 方法。ViewResolver
:解析視圖名稱到實際視圖。
關(guān)鍵代碼(WebMvcAutoConfiguration
):
@Bean @Primary @ConditionalOnMissingBean(DispatcherServlet.class) public DispatcherServlet dispatcherServlet(WebMvcProperties properties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(properties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(properties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(properties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(properties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(properties.isLogRequestDetails()); return dispatcherServlet; }
6. 最終啟動結(jié)果
- 嵌入式服務(wù)器(如 Tomcat)啟動并監(jiān)聽指定端口(默認(rèn) 8080)。
DispatcherServlet
注冊到 Servlet 容器,作為所有請求的入口。- Spring 上下文初始化完成,所有 Bean 已加載并可用。
ApplicationReadyEvent
發(fā)布,標(biāo)志應(yīng)用可處理外部請求。
總結(jié):啟動流程關(guān)鍵點(diǎn)
SpringApplication
初始化:推斷應(yīng)用類型、加載初始化器和監(jiān)聽器。- 環(huán)境配置:加載
application.properties
等配置源。 ApplicationContext
創(chuàng)建:根據(jù) Web 類型選擇相應(yīng)的上下文實現(xiàn)。- 自動配置:基于依賴和條件注解,自動配置 Web 組件(如
DispatcherServlet
)。 - 嵌入式服務(wù)器啟動:通過
ServletWebServerFactory
創(chuàng)建并啟動 Tomcat/Jetty。 - Spring MVC 初始化:配置請求映射、視圖解析等核心組件。
通過這種機(jī)制,Spring Boot 實現(xiàn)了“零配置”啟動 Web 項目的能力,開發(fā)者只需關(guān)注業(yè)務(wù)邏輯,無需手動處理服務(wù)器配置和組件裝配。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- SpringBoot?main方法結(jié)束程序不停止的原因分析及解決方法
- Springboot解決no main manifest attribute錯誤
- SpringBoot在啟動類main方法中調(diào)用service層方法報“空指針異常“的解決辦法
- springboot項目啟動的時候,運(yùn)行main方法報錯NoClassDefFoundError問題
- springboot項目test文件夾下帶main方法的類不能運(yùn)行問題
- SpringBoot啟動異常Exception in thread “main“ java.lang.UnsupportedClassVersionError
- Springboot使用maven打包指定mainClass問題
相關(guān)文章
Java8中關(guān)于Function.identity()的使用
這篇文章主要介紹了Java8中關(guān)于Function.identity()的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Spring整合Quartz Job以及Spring Task的實現(xiàn)方法
下面小編就為大家分享一篇Spring整合Quartz Job以及Spring Task的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Spring Boot與Kotlin定時任務(wù)的示例(Scheduling Tasks)
這篇文章主要介紹了Spring Boot與Kotlin定時任務(wù)的示例(Scheduling Tasks),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03java正則表達(dá)式優(yōu)化超詳細(xì)舉例講解
正則表達(dá)式是一種強(qiáng)大的文本處理工具,在數(shù)據(jù)驗證、字符串搜索和替換等方面有廣泛應(yīng)用,這篇文章主要介紹了java正則表達(dá)式優(yōu)化的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-07-07SpringBoot 中實現(xiàn)跨域的5種方式小結(jié)
這篇文章主要介紹了SpringBoot 中實現(xiàn)跨域的5種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02