SpringBoot 嵌入式web容器的啟動(dòng)原理詳解
SpringBoot應(yīng)用啟動(dòng)run方法
SpringApplication.java 中執(zhí)行的代碼
@SpringBootApplication
@EnableAsync //使用異步注解@Async 需要在這里加上@EnableAsync
@MapperScan("springboot.dao") //不可或缺作用是掃描dao包下面的所有mapper裝配
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class,args);
}
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}

private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}
ServletWebServerApplicationContext.java執(zhí)行的方法
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
} catch (RuntimeException var2) {
this.stopAndReleaseWebServer();
throw var2;
}
}
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = this.getWebServerFactory();
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
protected ServletWebServerFactory getWebServerFactory() {
String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
} else if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
} else {
return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
}
//配置嵌入式的servlet容器
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> MyCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
SpringBoot 2.x 版本
嵌入式Servlet容器自動(dòng)配置原理以及啟動(dòng)原理
一、版本說(shuō)明
Spring Boot 2.x 版本的嵌入式Servlet容器自動(dòng)配置是通過(guò) WebServerFactoryCustomizer定制器 來(lái)定制的,而在Spring Boot 1.x 版本中我們是通過(guò) EmbeddedServletContainerCustomizer 嵌入式的Servlet容器定制器來(lái)定制的。由于之前看的資料都是1.x的版本,但是我使用的是2.x,所以在這里記錄一下2.x版本的嵌入式Servlet容器自動(dòng)配置原理以及啟動(dòng)原理。
二、總結(jié)
嵌入式Servlet容器自動(dòng)配置原理以及啟動(dòng)原理有三大步:
步驟:
- SpringBoot 根據(jù)導(dǎo)入的依賴信息,自動(dòng)創(chuàng)建對(duì)應(yīng)的 WebServerFactoryCustomizer(web服務(wù)工廠定制器);
- WebServerFactoryCustomizerBeanPostProcessor(web服務(wù)工廠定制器組件的后置處理器)獲取所有類型為web服務(wù)工廠定制器的組件(包含實(shí)現(xiàn)WebServerFactoryCustomizer接口,自定義的定制器組件),依次調(diào)用customize()定制接口,定制Servlet容器配置;
- 嵌入式的Servlet容器工廠創(chuàng)建tomcat容器,初始化并啟動(dòng)容器。
三、嵌入式Servlet容器自動(dòng)配置原理(以Tomcat為例)
1、首先找到 EmbeddedWebServerFactoryCustomizerAutoConfiguration ,在里面我們可以看到SpringBoot支持的 servlet容器
//SprinBoot支持的servlet容器有三個(gè)Tomcat、Jetty、Undertow,但是默認(rèn)配置的是Tomcat
//嵌入式的Undertow
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Undertow.class, SslClientAuthMode.class})
public static class UndertowWebServerFactoryCustomizerConfiguration {
public UndertowWebServerFactoryCustomizerConfiguration() {
}
@Bean
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
//嵌入式的Jetty
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Server.class, Loader.class, WebAppContext.class})
public static class JettyWebServerFactoryCustomizerConfiguration {
public JettyWebServerFactoryCustomizerConfiguration() {
}
@Bean
public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
//嵌入式的Tomcat
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
public static class TomcatWebServerFactoryCustomizerConfiguration {
public TomcatWebServerFactoryCustomizerConfiguration() {
}
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
2、準(zhǔn)備環(huán)節(jié)
1)在以下位置打一個(gè)斷點(diǎn)

2)、點(diǎn)進(jìn) TomcatWebServerFactoryCustomizer 也就是上圖 return 的,然后在里面的如下位置打一個(gè)斷點(diǎn)

3)、然后在里面debug程序,我們?cè)诳刂婆_(tái)就可以看到如下信息

3、按照上圖從下往上分析。我們啟動(dòng)springboot應(yīng)用時(shí),都是直接運(yùn)行主程序的main方法,然后調(diào)用里面的run方法,如下圖

4、調(diào)用完run方法,回來(lái)到 refreshContext 方法,這個(gè)方法是幫我們創(chuàng)建IOC容器對(duì)象,并且初始化容器,創(chuàng)建容器中的每一個(gè)組件

5、在調(diào)用了 reflesh 方法刷新剛才的IOC容器后,來(lái)到 onreflesh 方法,調(diào)用createWebServer()方法,創(chuàng)建WebServer

6、來(lái)到createWebServer()方法,該方法最終能夠獲取到一個(gè)與當(dāng)前應(yīng)用(也就是總結(jié)里說(shuō)的第一步,根據(jù)我們導(dǎo)入的依賴來(lái)獲?。┧鶎?dǎo)入的Servlet類型相匹配的web服務(wù)工廠,通過(guò)工廠就可以獲取到相應(yīng)的 WebServerFactoryCustomizer (Web服務(wù)工廠定制器)
注:createWebServer()執(zhí)行后,我們其實(shí)來(lái)到了 EmbeddedWebServerFactoryCustomizerAutoConfiguration,然后根據(jù)條件(配置的依賴)配置哪一個(gè)Web服務(wù)器

我們通過(guò)查看 ServletWebServerFactory 的子類,可以看到其中三個(gè)就是Tomcat、Jetty和Undertow,根據(jù)我們的配置,所以這里獲取到的是 TomcatWebServerFactoryCustomizer

至此,TomcatWebServerFactoryCustomizer組件創(chuàng)建完成,對(duì)應(yīng)的服務(wù)配置類也已添加到IOC容器。
7、因?yàn)槿萜髦心硞€(gè)組件要創(chuàng)建對(duì)象就會(huì)驚動(dòng)后置處理器 然后就到 WebServerFactoryCustomizerBeanPostProcessor(web服務(wù)工廠定制器組件的后置處理器),該類負(fù)責(zé)在bean組件初始化之前執(zhí)行初始化工作。它先從IOC容器中獲取所有類型為WebServerFactoryCustomizerBeans(web服務(wù)工廠定制器的組件)

通過(guò)后置處理器獲取到的TomcatWebServerFactoryCustomizer調(diào)用customize()定制方法,獲取到Servlet容器相關(guān)配置類ServerProperties,進(jìn)行自動(dòng)配置

至此,嵌入式Servlet容器的自動(dòng)配置完成。
注:從源碼分析可以得出配置嵌入式Servlet容器的兩種解決方案:
1、在全局配置文件中,通過(guò)server.xxx來(lái)修改和server有關(guān)的配置:
server.port=8081 server.tomcat.xxx...
2、實(shí)現(xiàn)WebServerFactoryCustomizer接口,重寫(xiě)它的customize()方法,對(duì)容器進(jìn)行定制配置:
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
四、嵌入式Servlet容器啟動(dòng)原理(以Tomcat為例)
1、應(yīng)用啟動(dòng)后,根據(jù)導(dǎo)入的依賴信息,創(chuàng)建了相應(yīng)的Servlet容器工廠,創(chuàng)建了TomcatServletWebServerFactory,調(diào)用getWebServer()方法創(chuàng)建Tomcat容器:(其實(shí)就是重寫(xiě)了ServletWebServerFactory里面的getWebServer方法)

找到下面的getTomcatWebServer方法

2、然后點(diǎn)進(jìn)去分析TomcatWebServer的有參構(gòu)造器,執(zhí)行 initialize() 方法

3、點(diǎn)進(jìn)去就可以發(fā)現(xiàn),里面通過(guò)調(diào)用start方法來(lái)啟動(dòng)Tomcat

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA實(shí)戰(zhàn)練習(xí)之圖書(shū)管理系統(tǒng)實(shí)現(xiàn)流程
隨著網(wǎng)絡(luò)技術(shù)的高速發(fā)展,計(jì)算機(jī)應(yīng)用的普及,利用計(jì)算機(jī)對(duì)圖書(shū)館的日常工作進(jìn)行管理勢(shì)在必行,本篇文章手把手帶你用Java實(shí)現(xiàn)一個(gè)圖書(shū)管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-10-10
Spring 中使用反射創(chuàng)建 Bean 實(shí)例的幾種方式
文章介紹了在Spring框架中如何使用反射來(lái)創(chuàng)建Bean實(shí)例,包括使用Class.newInstance()、Constructor.newInstance()、工廠方法以及Spring的BeanUtils工具類,文章還強(qiáng)調(diào)了反射操作的注意事項(xiàng),如異常處理、性能、安全性以及類型安全,感興趣的朋友一起看看吧2025-03-03
Java實(shí)現(xiàn)單鏈表反轉(zhuǎn)的多種方法總結(jié)
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)單鏈表反轉(zhuǎn)的多種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
nacos注冊(cè)中心單節(jié)點(diǎn)ap架構(gòu)源碼解析(最新推薦)
這篇文章主要介紹了nacos注冊(cè)中心單節(jié)點(diǎn)ap架構(gòu)源碼解析,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
解決dubbo注冊(cè)到zookeeper速度慢的問(wèn)題
這篇文章主要介紹了解決dubbo注冊(cè)到zookeeper速度慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04

