SpringBoot通過main方法啟動(dòng)web項(xiàng)目實(shí)踐
Spring Boot 通過 main 方法啟動(dòng) Web 項(xiàng)目的過程涉及多個(gè)核心組件和自動(dòng)化機(jī)制,下面從源碼角度詳細(xì)拆解:
1. 啟動(dòng)入口:SpringApplication.run()
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
SpringApplication.run() 是啟動(dòng)的核心入口,它主要完成以下工作:
2. SpringApplication初始化
// SpringApplication 構(gòu)造函數(shù)核心邏輯
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 1. 設(shè)置資源加載器
this.resourceLoader = resourceLoader;
// 2. 校驗(yàn)并保存主配置類(即 @SpringBootApplication 標(biāo)注的類)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 3. 推斷應(yīng)用類型(REACTIVE、SERVLET、NONE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 4. 加載并實(shí)例化 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 5. 加載并實(shí)例化 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)聽啟動(dòng)過程中的事件(如ApplicationStartingEvent)。
3. run()方法核心流程
public ConfigurableApplicationContext run(String... args) {
// 1. 計(jì)時(shí)和發(fā)布啟動(dòng)事件
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 2. 獲取并啟動(dòng)監(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. 刷新上下文(核心啟動(dòng)邏輯)
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ù)器啟動(dòng)關(guān)鍵點(diǎn)
4.1refreshContext()方法觸發(fā)服務(wù)器啟動(dòng)
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 實(shí)際類型為 AnnotationConfigServletWebServerApplicationContext,它繼承自 ServletWebServerApplicationContext,后者在 refresh() 過程中會(huì):
// ServletWebServerApplicationContext 核心方法
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 創(chuàng)建并啟動(dò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實(shí)例化服務(wù)器
以 Tomcat 為例,TomcatServletWebServerFactory 的 getWebServer() 方法會(huì):
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 創(chuàng)建 Tomcat 實(shí)例
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. 啟動(dòng)服務(wù)器
return getTomcatWebServer(tomcat);
}
5. Spring MVC 組件自動(dòng)配置
通過 WebMvcAutoConfiguration 自動(dòng)配置核心組件:
DispatcherServlet:作為前端控制器,處理所有 HTTP 請求。HandlerMapping:映射 URL 到具體的 Controller 方法。ViewResolver:解析視圖名稱到實(shí)際視圖。
關(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. 最終啟動(dòng)結(jié)果
- 嵌入式服務(wù)器(如 Tomcat)啟動(dòng)并監(jiān)聽指定端口(默認(rèn) 8080)。
DispatcherServlet注冊到 Servlet 容器,作為所有請求的入口。- Spring 上下文初始化完成,所有 Bean 已加載并可用。
ApplicationReadyEvent發(fā)布,標(biāo)志應(yīng)用可處理外部請求。
總結(jié):啟動(dòng)流程關(guān)鍵點(diǎn)
SpringApplication初始化:推斷應(yīng)用類型、加載初始化器和監(jiān)聽器。- 環(huán)境配置:加載
application.properties等配置源。 ApplicationContext創(chuàng)建:根據(jù) Web 類型選擇相應(yīng)的上下文實(shí)現(xiàn)。- 自動(dòng)配置:基于依賴和條件注解,自動(dòng)配置 Web 組件(如
DispatcherServlet)。 - 嵌入式服務(wù)器啟動(dòng):通過
ServletWebServerFactory創(chuàng)建并啟動(dòng) Tomcat/Jetty。 - Spring MVC 初始化:配置請求映射、視圖解析等核心組件。
通過這種機(jī)制,Spring Boot 實(shí)現(xiàn)了“零配置”啟動(dòng) Web 項(xiàng)目的能力,開發(fā)者只需關(guān)注業(yè)務(wù)邏輯,無需手動(dòng)處理服務(wù)器配置和組件裝配。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot?main方法結(jié)束程序不停止的原因分析及解決方法
- Springboot解決no main manifest attribute錯(cuò)誤
- SpringBoot在啟動(dòng)類main方法中調(diào)用service層方法報(bào)“空指針異?!暗慕鉀Q辦法
- springboot項(xiàng)目啟動(dòng)的時(shí)候,運(yùn)行main方法報(bào)錯(cuò)NoClassDefFoundError問題
- springboot項(xiàng)目test文件夾下帶main方法的類不能運(yùn)行問題
- SpringBoot啟動(dòng)異常Exception in thread “main“ java.lang.UnsupportedClassVersionError
- Springboot使用maven打包指定mainClass問題
相關(guān)文章
Java8中關(guān)于Function.identity()的使用
這篇文章主要介紹了Java8中關(guān)于Function.identity()的使用,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
Spring整合Quartz Job以及Spring Task的實(shí)現(xiàn)方法
下面小編就為大家分享一篇Spring整合Quartz Job以及Spring Task的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12
Spring的事務(wù)控制實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Spring的事務(wù)控制實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
Spring Boot與Kotlin定時(shí)任務(wù)的示例(Scheduling Tasks)
這篇文章主要介紹了Spring Boot與Kotlin定時(shí)任務(wù)的示例(Scheduling Tasks),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03
java正則表達(dá)式優(yōu)化超詳細(xì)舉例講解
正則表達(dá)式是一種強(qiáng)大的文本處理工具,在數(shù)據(jù)驗(yàn)證、字符串搜索和替換等方面有廣泛應(yīng)用,這篇文章主要介紹了java正則表達(dá)式優(yōu)化的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-07-07
SpringBoot 中實(shí)現(xiàn)跨域的5種方式小結(jié)
這篇文章主要介紹了SpringBoot 中實(shí)現(xiàn)跨域的5種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02

