SpringBoot Starter機制及整合tomcat的實現(xiàn)詳解
Starter機制和springboot整合tomcat
Starter機制
先解釋一下什么是Starter機制。Starter機制就是maven工程中pom文件引入了某個Starter依賴,就能使用對應(yīng)的功能 例如 引入web的starter依賴 ,就可以使用有關(guān)于web方面的功能。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
那么這個 Starter機制是怎么運作的呢?當(dāng)我們在項目的pom.xml文件中添加某個Starter依賴時,其實就是簡單的添加了很多其他的依賴,比如:
- 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自動配置的原理從META-INF/spring.factories中獲取到tomcat自動配置類。通過各種條件注解類判斷是否滿足加載tomcat自動配置類的條件。這樣就完成了 Starter機制
如果我們在項目中要用Tomcat,那就依賴spring-boot-starter-web就夠了,因為它默認依賴了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的自動配置機制、Starter機制,條件注解分析等,我們拿一個實際的業(yè)務(wù)案例來串講一下,那就是SpringBoot和Tomcat的整合。
我們知道,只要我們的項目添加的starter為:spring-boot-starter-web,那么我們啟動項目時,SpringBoot就會自動啟動一個Tomcat。
那么這是怎么做到的呢?
首先我們可以發(fā)現(xiàn),在spring-boot-starter-web這個starter中,其實間接的引入了spring-bootstarter-tomcat這個starter,這個spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,
所以只要我們項目中依賴了spring-boot-starter-web就相當(dāng)于依賴了Tomcat。
從這個spring-boot-starter-web依賴中引入了tomcat的依賴
又從tomcat的依賴中引入了tomcat相關(guān)的依賴
那么來看下springboot如何啟動web容器的。
首先我們知道springboot啟動通過SpringApplication.run(MyApplication.class, args);
,進入run方法,會調(diào)到如下代碼
這個run方法中的有一個refreshContext(context);
方法。refreshContext(context);
方法經(jīng)過一系列調(diào)用到ServletWebServerApplicationContext
的onRefresh()
方法
@Override protected void onRefresh() { super.onRefresh(); try { // 啟動Tomcat createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
重點就是createWebServer();
方法。這個方法中就啟動了tomcat
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //ServletWebServerFactory 一個接口 實現(xiàn)類有TomcatServletWebServerFactory 等等容器工廠 //通過getWebServerFactory() 獲取到對應(yīng)容器的創(chuàng)建工廠 ServletWebServerFactory factory = getWebServerFactory(); //通過實現(xiàn)類的getWebServer來啟動對應(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());
。我們一步步分析這兩個方法。
首先ServletWebServerFactory 是一個接口,它的實現(xiàn)類有
那么它會用到哪個實現(xiàn)類呢?這個就和ServletWebServerFactory factory = getWebServerFactory();
方法有關(guān)
protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy //獲取到ServletWebServerFactory 所有實現(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 表示配置了多個容器 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); }
這個方法中,先獲取所有ServletWebServerFactory類型的beanName,如果為空或者大于一個,就會拋異常,也就是說如果配置了tomcat又配置jetty,就會有兩個ServletWebServerFactory類型的beanName,那么就會拋出異常。
判斷通過后,通過beanName和類型從bean工廠中獲取到唯一的ServletWebServerFactory,然后返回。重點就是ServletWebServerFactory類型的bean是什么時候配置進去的。這個就是涉及到web的自動配置類。
在MATE-INF/spring.factories中有一個ServletWebServerFactoryAutoConfiguration
的自動配置類。
@EnableConfigurationProperties(ServerProperties.class)注解會去加載ServerProperties
@ConfigurationProperties注解會從配置文件中讀取對應(yīng)的信息到ServerProperties的屬性上,所以我們在配置文件定義端口號,地址等等就會被識別。
ServletWebServerFactoryAutoConfiguration
的自動配置類通過@Import注解導(dǎo)入了ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
EmbeddedTomcat.class定義了TomcatServletWebServerFactory,所以回到之前的getWebServerFactory()
,拿到的就是TomcatServletWebServerFactory。然后調(diào)用factory.getWebServer
來啟動容器。實際上就是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); }
這個方法一目了然,就是new Tomcat()
,然后啟動。
這大致就是springboot整合tomcat的流程。如果整合的是jetty,那么拿到的ServletWebServerFactory類型就是JettyServletWebServerFactory,過程是一樣的。
總結(jié)
springboot整合tomcat的過程中,首先通過依賴導(dǎo)入了tomcat的相關(guān)依賴。通過ServletWebServerFactoryAutoConfiguration
自動配置類,加載EmbeddedTomcat,也就是加載了TomcatServletWebServerFactory。通過TomcatServletWebServerFactory的getWebServer方法啟動tomcat。
到此這篇關(guān)于SpringBoot Starter機制及整合tomcat的實現(xiàn)詳解的文章就介紹到這了,更多相關(guān)SpringBoot Starter機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot?Reactor?整合?Resilience4j詳析
這篇文章主要介紹了Spring?Boot?Reactor整合Resilience4j詳析,文章通過引入pom包展開詳細介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-09-09IntelliJ?IDEA設(shè)置JVM運行參數(shù)的圖文介紹
這篇文章主要介紹了IntelliJ?IDEA設(shè)置JVM運行參數(shù)的方法,包括配置方式及優(yōu)先級,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04Java輸入學(xué)號、姓名、年齡并對其進行輸出的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Java輸入學(xué)號、姓名、年齡并對其進行輸出的實現(xiàn)方法,在計算機編程中,輸出學(xué)號和姓名是一個常見的任務(wù),文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-09-09Java編程實現(xiàn)軌跡壓縮之Douglas-Peucker算法詳細代碼
這篇文章主要介紹了Java編程實現(xiàn)軌跡壓縮之Douglas-Peucker算法詳細代碼,具有一定借鑒價值,需要的朋友可以參考。2017-11-11解決java.lang.Error: Unresolved compilation pro
這篇文章主要介紹了解決java.lang.Error: Unresolved compilation problems:問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03Spring Boot 開發(fā)私有即時通信系統(tǒng)(WebSocket)
本文利用Spring Boot作為基礎(chǔ)框架,Spring Security作為安全框架,WebSocket作為通信框架,實現(xiàn)點對點聊天和群聊天2017-04-04