SpringBoot通過main方法啟動web項目實踐
Spring Boot 通過 main 方法啟動 Web 項目的過程涉及多個核心組件和自動化機制,下面從源碼角度詳細(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)用運行中事件
listeners.running(context);
return context;
}
4. 嵌入式 Web 服務(wù)器啟動關(guā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)鍵點
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 初始化:配置請求映射、視圖解析等核心組件。
通過這種機制,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層方法報“空指針異?!暗慕鉀Q辦法
- springboot項目啟動的時候,運行main方法報錯NoClassDefFoundError問題
- springboot項目test文件夾下帶main方法的類不能運行問題
- SpringBoot啟動異常Exception in thread “main“ java.lang.UnsupportedClassVersionError
- Springboot使用maven打包指定mainClass問題
相關(guān)文章
SpringBoot教程_創(chuàng)建第一個SpringBoot項目
這篇文章主要介紹了SpringBoot教程_創(chuàng)建第一個SpringBoot項目,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
使用FeignClient進(jìn)行微服務(wù)交互方式(微服務(wù)接口互相調(diào)用)
這篇文章主要介紹了使用FeignClient進(jìn)行微服務(wù)交互方式(微服務(wù)接口互相調(diào)用),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring Cloud Gateway打造可擴展的微服務(wù)網(wǎng)關(guān)
微服務(wù)網(wǎng)關(guān)是一個位于客戶端和后端微服務(wù)之間的服務(wù)器,用于處理所有與客戶端的通信,Spring Cloud Gateway都是一個值得考慮的選擇,它將幫助您更好地管理和保護(hù)您的微服務(wù),感興趣的朋友一起看看吧2023-11-11
Spring調(diào)用this導(dǎo)致事務(wù)失效的三種解決方案
在 Spring 事務(wù)管理 中,如果你在同一個類的方法內(nèi)部使用 this.xxx() 調(diào)用另一個帶 @Transactional 的方法,事務(wù)很可能會失效,這不是玄學(xué),而是代理機制在背后打盹了,本文就給大家介紹如何解決this調(diào)用導(dǎo)致事務(wù)失效問題,需要的朋友可以參考下2026-02-02
一小時迅速入門Mybatis之Prepared Statement與符號的使用
這篇文章主要介紹了一小時迅速入門Mybatis之Prepared Statement與符號的使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09

