SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解
Starter機(jī)制和springboot整合tomcat
Starter機(jī)制
先解釋一下什么是Starter機(jī)制。Starter機(jī)制就是maven工程中pom文件引入了某個(gè)Starter依賴,就能使用對(duì)應(yīng)的功能 例如 引入web的starter依賴 ,就可以使用有關(guān)于web方面的功能。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
那么這個(gè) Starter機(jī)制是怎么運(yùn)作的呢?當(dāng)我們在項(xiàng)目的pom.xml文件中添加某個(gè)Starter依賴時(shí),其實(shí)就是簡單的添加了很多其他的依賴,比如:
- spring-boot-starter-web:引入了spring-boot-starter、spring-boot-starter-json、spring-boot-starter-tomcat等和Web開發(fā)相關(guān)的依賴包
- spring-boot-starter-tomcat:引入了tomcat-embed-core、tomcat-embed-el、tomcat-embed-websocket等和Tomcat相關(guān)的依賴包
通過springboot自動(dòng)配置的原理從META-INF/spring.factories中獲取到tomcat自動(dòng)配置類。通過各種條件注解類判斷是否滿足加載tomcat自動(dòng)配置類的條件。這樣就完成了 Starter機(jī)制
如果我們在項(xiàng)目中要用Tomcat,那就依賴spring-boot-starter-web就夠了,因?yàn)樗J(rèn)依賴了spring-boot-starter-tomcat,從而依賴了Tomcat,從而Tomcat相關(guān)的Bean能生效。
而如果不想用Tomcat,那就得這么寫:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

springboot整合tomcat
通過前面SpringBoot的自動(dòng)配置機(jī)制、Starter機(jī)制,條件注解分析等,我們拿一個(gè)實(shí)際的業(yè)務(wù)案例來串講一下,那就是SpringBoot和Tomcat的整合。
我們知道,只要我們的項(xiàng)目添加的starter為:spring-boot-starter-web,那么我們啟動(dòng)項(xiàng)目時(shí),SpringBoot就會(huì)自動(dòng)啟動(dòng)一個(gè)Tomcat。
那么這是怎么做到的呢?
首先我們可以發(fā)現(xiàn),在spring-boot-starter-web這個(gè)starter中,其實(shí)間接的引入了spring-bootstarter-tomcat這個(gè)starter,這個(gè)spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,
所以只要我們項(xiàng)目中依賴了spring-boot-starter-web就相當(dāng)于依賴了Tomcat。
從這個(gè)spring-boot-starter-web依賴中引入了tomcat的依賴

又從tomcat的依賴中引入了tomcat相關(guān)的依賴

那么來看下springboot如何啟動(dòng)web容器的。
首先我們知道springboot啟動(dòng)通過SpringApplication.run(MyApplication.class, args);,進(jìn)入run方法,會(huì)調(diào)到如下代碼

這個(gè)run方法中的有一個(gè)refreshContext(context);方法。refreshContext(context);方法經(jīng)過一系列調(diào)用到ServletWebServerApplicationContext的onRefresh()方法
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 啟動(dòng)Tomcat
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
重點(diǎn)就是createWebServer();方法。這個(gè)方法中就啟動(dòng)了tomcat
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//ServletWebServerFactory 一個(gè)接口 實(shí)現(xiàn)類有TomcatServletWebServerFactory 等等容器工廠
//通過getWebServerFactory() 獲取到對(duì)應(yīng)容器的創(chuàng)建工廠
ServletWebServerFactory factory = getWebServerFactory();
//通過實(shí)現(xiàn)類的getWebServer來啟動(dòng)對(duì)應(yīng)的容器
this.webServer = factory.getWebServer(getSelfInitializer());
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}createWebServer();中核心代碼是ServletWebServerFactory factory = getWebServerFactory();和this.webServer = factory.getWebServer(getSelfInitializer());。我們一步步分析這兩個(gè)方法。
首先ServletWebServerFactory 是一個(gè)接口,它的實(shí)現(xiàn)類有

那么它會(huì)用到哪個(gè)實(shí)現(xiàn)類呢?這個(gè)就和ServletWebServerFactory factory = getWebServerFactory();方法有關(guān)
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
//獲取到ServletWebServerFactory 所有實(shí)現(xiàn)類的bean的名稱 也就是springboot中配置了哪些容器 如tomcat jetty等
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
//如果沒有bean 表示沒有配置容器
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
//如果bean大于1 表示配置了多個(gè)容器
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
//最終返回唯一的容器工廠 比如配置了tomcat 那么就返回了TomcatServletWebServerFactory
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
這個(gè)方法中,先獲取所有ServletWebServerFactory類型的beanName,如果為空或者大于一個(gè),就會(huì)拋異常,也就是說如果配置了tomcat又配置jetty,就會(huì)有兩個(gè)ServletWebServerFactory類型的beanName,那么就會(huì)拋出異常。
判斷通過后,通過beanName和類型從bean工廠中獲取到唯一的ServletWebServerFactory,然后返回。重點(diǎn)就是ServletWebServerFactory類型的bean是什么時(shí)候配置進(jìn)去的。這個(gè)就是涉及到web的自動(dòng)配置類。
在MATE-INF/spring.factories中有一個(gè)ServletWebServerFactoryAutoConfiguration的自動(dòng)配置類。

@EnableConfigurationProperties(ServerProperties.class)注解會(huì)去加載ServerProperties

@ConfigurationProperties注解會(huì)從配置文件中讀取對(duì)應(yīng)的信息到ServerProperties的屬性上,所以我們在配置文件定義端口號(hào),地址等等就會(huì)被識(shí)別。
ServletWebServerFactoryAutoConfiguration的自動(dòng)配置類通過@Import注解導(dǎo)入了ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,

EmbeddedTomcat.class定義了TomcatServletWebServerFactory,所以回到之前的getWebServerFactory(),拿到的就是TomcatServletWebServerFactory。然后調(diào)用factory.getWebServer來啟動(dòng)容器。實(shí)際上就是TomcatServletWebServerFactory的getWebServer方法
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}這個(gè)方法一目了然,就是new Tomcat(),然后啟動(dòng)。
這大致就是springboot整合tomcat的流程。如果整合的是jetty,那么拿到的ServletWebServerFactory類型就是JettyServletWebServerFactory,過程是一樣的。
總結(jié)
springboot整合tomcat的過程中,首先通過依賴導(dǎo)入了tomcat的相關(guān)依賴。通過ServletWebServerFactoryAutoConfiguration自動(dòng)配置類,加載EmbeddedTomcat,也就是加載了TomcatServletWebServerFactory。通過TomcatServletWebServerFactory的getWebServer方法啟動(dòng)tomcat。
到此這篇關(guān)于SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)SpringBoot Starter機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java通過cellstyle屬性設(shè)置Excel單元格常用樣式的全面總結(jié)講解
在處理Excel文件時(shí),經(jīng)常需要對(duì)單元格進(jìn)行樣式設(shè)置,以滿足特定的需求和美化要求,這篇文章主要給大家介紹了關(guān)于Java通過cellstyle屬性設(shè)置Excel單元格常用樣式的相關(guān)資料,需要的朋友可以參考下2024-01-01
使用maven構(gòu)建java9 service實(shí)例詳解
本篇文章主要介紹了使用maven構(gòu)建java9 service實(shí)例詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02
簡單實(shí)現(xiàn)Java通訊錄系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何簡單實(shí)現(xiàn)Java通訊錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
mybatis 有時(shí)update語句執(zhí)行無效的解決方案
這篇文章主要介紹了在項(xiàng)目里mybatis有時(shí)update語句執(zhí)行無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧2024-08-08
Java中ByteArrayOutputStream亂碼問題解決
本文主要介紹了Java中ByteArrayOutputStream亂碼問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07

