webserver接口_SpringBoot內置源碼解析WebServer初始化過程

WebServer 初始化過程

在上一節中 Spring Boot 初始化了 WebServer 對應的工廠類。同時,我們也知道對應 Web容器的WebServer實現類有:TomcatWebServer、JettyWebServer和UndertowWebServer。

這節重點講解這些 WebServer 是如何被初始化,又如何啟動的。

WebServer 接口的源代碼如下。

public interface WebServer {void start() throws WebServerException;void stop() throws WebServerException;int getPort();}

接口定義了 3 個方法: start 方 法為啟動容器,stop 方 法為停止容器,getPort 方法為獲得容器端口。

245cf63796d584d0b79dd93feadae2ae.png

現在以 Tomcat 的啟動為例來說明整個內置容器的加載與啟動。在上節中,工廠類已經被自動配置初始化。那么,在什么地方用到它們的呢?這要回到最初 Spring Boot 啟動的過程中。

還記得 SpringApplication 的 run 方法中有一個調用初始化容器的方法 refreshContext 嗎?

我們就從這個方法開始追蹤。

public ConfigurableApplicationContext run(String... args) {try {//初始化容器refreshContext(context);} catch (Throwable ex) {}

在 run 方法中調用了 refreshContext 方法,refreshContext 方法中又調用了refresh 方法。

private void refreshContext(ConfigurableApplicationContext context) {//調用 refresh 方法refresh(context);refresh 方法的代碼如下。protected void refresh(ApplicationContext applicationContext) {Assert. isInstanceOf(AbstractApplicat ionContext. class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}

通過 refresh 方法我們能看到什么呢?對的,就是 AbstractApplicationContext 這個抽象類,該類的實例化對象在調用 refreshContext 方法之前,已經通過 createApplicationContext 方法進行實例化了。createApplicationContext 方法的源代碼如下。

protected ConfigurableApplicat ionContext createApplicationContext() {//首先獲取容器的類變量Class> contextClass = this . applicat ionContextClass;/如果為 null,則根據 web 應用類型按照默認類進行創建if (contextClass == null) {trywitch (this . webApplicat ionType) {contextClass = Class . forName(DEFAULT_ _SERVLET _WEB_ _CONTEXT_ CLASS);break;case REACTIVE:contextClass = Class . forName (DEFAULT_ REACTIVE_ WEB_ CONTEXT_ CLASS);break;default:contextClass = Class . forName (DEFAULT CONTEXT. CLASS);}catch (ClassNotFoundException ex) {如果存在對應的 Class 配置,則通過 Spring 提 供的 BeanUtils 來進行實例化 return(ConfigurableApplicationContext) BeanUtils. instantiateClass(contextClass);}}}

ServletWeb 項目默認會實例化 DEFAULT_ _SERVLET_ WEB_ _CONTEXT _CLASS 常量指定的 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 類。

在 refresh 方法中調用的 AbstractApplicationContext 的 refresh 方法就是這個常量配置的類的 refresh 方法但 AnnotationConfigServletWebServerApplicationContext 方法內并沒有該refresh 方法,該方法定義在它的父類 ServletWebServerApplicationContext 中。

@Overridepublic final void refresh() throws BeansException, IllegalStateExceptiontry {super . refresh();} catch (RuntimeException ex) {stopAndReleaseWebServer();throw ex;}}

ServletWebServerApplicationContext 的 refresh 方 法 僅 調 用 了 父 類AbstractApplication-Context 中的 refresh 方法。AbstractApplicationContext 中的 refresh方法的代碼如下。

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this . startupShutdownMonitor)try {...onRefresh();}catch (BeansException ex) {}

忽略掉 refresh 方法中的其他方法,我們重點了解下其調用的 onRefresh 方法。

protected void onRefresh() throws BeansException {//為子類提供,默認不做任何操作我們發現這個 onRefresh 方法默認是空的,待其子類來實現。也就是說,該方法真正的實現又回到了它的子類 ServletWebServerApplicationContext 中。@Overrideprotected void onRefresh() {super . onRefresh();try {createWebServer();} catch (Throwable ex){throw new ApplicationContextException("Unable to start web server", ex);}

經過一路的代碼跟蹤,終于回到重點方法: createWebServer 方法。

private void createWebServer() {WebServer webServer = this . webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletwebServerFactory factory = getWebServerFactory();this . webServer = factory . getWebServer(getSelfInitializer());} else if (servletContext != null) {initPropertySources();}}

在 ServletWebServerApplicationContext 的 createWebServer 方 法 中 , 初 始 化 時 默 認web-Server 和 servletContext 都為 null,因此直接進入第一個 if 判斷中的業務邏輯。看一下get-WebServerFactory 都做 了些什么。

protected ServletWebServerFactory getWebServerFactory() {//使用 Bean name 數組的好處是可以不用考慮層級關系String[] beanNames = getBeanF actory() . getBeanNamesForType(ServletWebServer-Factory.class);if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServe-rApplicationContext due to missing" +"ServletWebServerFactory bean.");if (beanNames . length > 1) {throw new ApplicationContextException("Unable to start ServletwebServer-ApplicationContext due to multiple”+ "ServletWebServerFactory beans :”+StringUtils.arrayToCommaDelimitedString(beanNames));}return getBeanFactory(). getBean(beanNames[0], ServletWebServerFactory.class);}

getWebServerFactory 方法中通過 BeanFactory 獲得類型為 ServletWebServerFactory 類的 beanNames 數組,然后判斷數組長度。當 beanNames 長度為 0 時,說明容器中沒有對應的 Bean 存在,則拋出異常;當 beanNames 長度大于 1 時,說明存在多個對應的 Bean,也就是說有可能同時存在多個 Web 容器的工廠方法,同樣拋出異常;只有 beanNames 長度等于 1 時,說明恰好存在一個對應的 Bean, 才會獲取對應的 Bean 并返回。

如果一層層向上追溯 TomcatServletWebServerFactory 的類結構,我們就會發現,它先是繼承了抽象類AbstractServletWebServerFactory,而抽象類AbstractServletWebServerFactory 又實現了口 ConfigurableServletWebServerFactory 接口ConfigurableServletWebServer-Factory又繼承接口ServletWebServerFactory.這里獲得的 ServletWebServerFactory 的具體實現類,正是我們在上一節中通過自動配置實例化的TomcatServletWebServerFactory 對象的 Bean 名稱。

當獲得 ServletWebServerFactory 之后,便調用了它的 getWebServer 方法,以 Tomcat 為例,其實也就是調用了 TomcatServletWebServerFactory 的 getWebServer 方法。

@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers)//內置 Tomcat 包中提供的類Tomcat tomcat = new Tomcat();//獲取并沒置 baseDir 路徑,不存在則創建一個 ltomcat 為前綴的臨時文件File baseDir = (this. baseDirectory != null) ? this. baseDirectorycreateTempDir("tomcat");tomcat . setBaseDir(baseDir . getAbsolutePath());/創建 ConnectorConnector connector = new Connector(this . protocol);tomcat . getService(). addConnector(connector);// Connector 定制化customi zeConnector(connector);tomcat. setConnector( connector);tomcat. . getHost(). setAutoDeploy(false);configureEngine(tomcat. getEngine());for (Connector additionalConnector : this . additionalTomcatConnectors) {tomcat . getService() . addConnector ( additionalConnector);prepareContext(tomcat . getHost(), initializers);//創建 omcatWebServerreturn getTomcatwebServer(tomcat);}}

TomcatServletWebServerFactory 的 getWebServer 方法中實現了 Tomcat 的創建、BaseDir的設置 、Connector 的初始化和定制化等一系 列初始化操作。

至此,上面代碼中依舊沒有 體現 TomcatServer 的創建和初始化, 不要著急,它們就在getWebServer 方法的最后- -行代碼調用的 getTomcatWebServer 方法中。

protected TomcatWebServer getTomcatWebServer (Tomcat tomcat) {return new TomcatWebServer(tomcat, getPort() >=

getTomcatWebServer 方法的實現很簡單,將 getWebServer 中創建的 Tomcat 對象和當前類中 port 值是否大于等于 0 的判斷結果作為 TomcatWebServer 構造方法的參數傳入,創建 TomcatWebServer 對象。

針 對 getTomcatWebServer 方 法 , 子 類 可 以 重 寫 該 方 法 , 返 回 一 個 不 同 的Tomcat-WebServer 或者添加針對 Tomcat Server 的一些額外操作。

先看 TomcatWebServer 的構造方法源碼。

public class TomcatWebServer implements WebServer {private final Tomcat tomcat;private final boolean autoStart;public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert . notNull(tomcat, "Tomcat Server must not be null");this. tomcat = tomcat;this. autoStart = autoStart;initialize();}}

構造方法接收 Tomcat tomcat 和 boolean autoStart 兩個參數,并將其賦值給對應的成員變量。其中 Tomcat 參數不能為 null, autoStart 參 數則根據端口是否大于等于 0 來決定是否啟動服務。在構造方法的最后,調用了 nitialize 方法來進行初始化操作。

412d3eb4e37ca23687fe2fc3e6bdb4d2.png

omcatWebServern 的 initialize 方法源代碼如下。

public class TomcatWebServer implements WebServer {private final Tomcat tomcat;private final boolean autoStart;privatevolatile boolean started;logger. info("Tomcat initialized with port(s): ”+ getPortsDescription(fsynchronized (this .monitor) {// 將 實 例 id 添 加 到 tomcat 引 擎 名 字 中 , 格 式 為 “ 原 引 / 擎 名 字 實 例 id”Contextcontext=findContext()://添加生命周期的監昕事件context . addL ifecyclelistener((event) -> {if (context . equals(event. getSource()) && Lifecycle . START EVENT.equals(event . getType()))//移除 connector, 磅保當服務器啟動時不會進行協議綁定})//啟動服務,觸發初始化監聽this. tomcat . start();/可以直接在主線程中重新拋出失敗異常, TomcatStarter 不存在或狀態錯誤均會拋出異常rethrowDeferredStartupExceptions();try/綁定一個命名的 context.到類加載器(), getClass().getClassLoader());sLoader(context, context. getNamingTokencatch (NamingException ex)// 當命名不可用時(拋異常), 直接跳過并繼續// 與 Jetty 不同, 所有 Tomcat 線程都是守護程序線程。創建一 一個阻止非守護程序止立即關閉startDaemonAwaitThread();} catch (Exception ex) {stopSilently();destroySilently();throw new WebServerException("Unable to start embedded Tomcat", e x);}}}}

通過以上源代碼,可以看出在 TomcatWebServer 的 initialize 方法中做了以下操作:重命名tomcat 弓|擎名稱、對 Context 添加生 命周期監聽事件、啟動服務觸發初始化監聽、檢查TomcatStarter 對象是否存在及 Container 狀態是否正確、綁定命名到類加載器、啟動守護等待線程等。

至此,針對 Tomcat 的 TomcatWebServer 的初始化已經完成。關于其他 Web 容器的WebServer 初始化操作,讀者可仿照本節的思路進行源代碼分析,這里不再逐一講解。

f6ec8dde371ea4c3f396b8ea5ffde971.png

本文給大家講解的內容是SpringBoot內置Servlet容器源碼解析:WebServer初始化過程

  1. 下篇文章給大家講解的是DispatcherServlet的加載過程;
  2. 覺得文章不錯的朋友可以轉發此文關注小編;
  3. 感謝大家的支持!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455366.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455366.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455366.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

提升應用程序彈性:保障工作負載正常運行

通過集群化、復制、快照、微服務和應用程序設計來提高企業工作負載的應用程序彈性和可用性。 應用程序的彈性和可用性是現代企業工作負載的關鍵屬性。應用程序需要在硬件故障發生后,扛過服務故障(例如負載平衡器和域名系統錯誤)保持工作狀態,并且可以忍受…

JDBC筆記01-JDBC,Connection,Statement,ResultSet,PreparedStatement,Properties

學習目標 理解JDBC原理 掌握Connection接口的使用 掌握Statement接口的使用 掌握ResultSet接口的使用 掌握PreparedStatement接口的使用 掌握Properties類與配置文件的使用 JDBC 概念 JDBC (Java DataBase Connectivity) Java數據庫連接技術的簡稱,提供連接各種常…

NVDKC6416平臺H.264算法優化

本文轉載自:http://blog.csdn.net/embedesign/archive/2009/09/15/4556486.aspx,版權歸原作者,編輯:小乙哥 多媒體通信終端設備具有廣泛的應用前景,可以應用于視頻會議、可視電話、PDA、數字電視等各個領域&#xff0…

攔截器及 Spring MVC 整合

一、實驗介紹 1.1 實驗內容 本節課程主要利用 Spring MVC 框架實現攔截器以及 Spring MVC 框架的整合。 1.2 實驗知識點 Spring MVC 框架攔截器1.3 實驗環境 JDK1.8Eclipse JavaEE二、實驗步驟 2.1 攔截器實現 在項目 hrms 的目錄 src/main/java 下新建包 com.shiyanlou.interc…

高德地圖軌跡回放_高德地圖上線了一個新功能….

文、路人甲TM德地圖這兩天剛上線了一個叫做「家人地圖」的功能,所謂家人地圖顧名思義,就是你可以通過高德地圖組建一個家人圈,在這個圈子里面你可以看到你的家人在什么位置,當你的家人到達什么位置的時候自動發送通知或者警告&…

You have new mail in /var/spool/mail/root消除提示的方法

有時在進入系統的時候經常提示You have new mail in /var/spool/mail/root 你覺得煩人---解決方法: 修改系統配置文件/etc/profile,告訴系統不要去檢查郵箱. 具體操作:命令行輸入:echo "unset MAILCHECK" >> /etc…

c3p0-config.xml文件簡單說明與備忘

<?xml version"1.0" encoding"UTF-8"?> <c3p0-config><named-config name"mysql"><!-- 配置數據庫用戶名 --><property name"user">root</property><!-- 配置數據庫密碼 --><property…

python 消息隊列 get是從隊首還是隊尾取東西_從零開始Python對redis作為消息隊列的使用...

一、Redis 服務1、安裝yum install redis2、 python安裝支持模塊/opt/python2.7.13/bin/pip install redis3、 和redis的簡單直接交互In [1]: import redisIn [2]: rc redis.Redis(host192.168.8.237,port6379,decode_responsesTrue)In [5]: rc.set(imoocc,jeson)Out[5]: True…

‘’和“”

單引號引的數據是char類型的 雙引號引的數據是String類型的 單引號只能引一個字符 而雙引號可以引0個及以上 字符&#xff08;Character&#xff09;是指人類語言的最小的表義符號&#xff0c;字符是指計算機中使用的字母、數字和符號&#xff0c;包括1、2、3、A、B、C、#、&am…

Spring整合Quartz定時任務 在集群、分布式系統中的應用(Mysql數據庫環境)

轉載&#xff1a;http://www.cnblogs.com/jiafuwei/p/6145280.html 單個Quartz實例能給予你很好的Job調度能力&#xff0c;但它不能滿足典型的企業需求&#xff0c;如可伸縮性、高可靠性滿足。假如你需要故障轉移的能力并能運行日益增多的 Job&#xff0c;Quartz集群勢必成為你…

JDBC筆記02-數據庫連接池 Spring JDBC

今日內容 數據庫連接池Spring JDBC : JDBC Template 數據庫連接池 概念: 其實就是一個容器(集合),存放數據庫連接的容器 當系統初始化好后,容器被創建,容器中會申請一些連接對象,當用戶來訪問數據庫時,從容器中獲取連接對象,用戶訪問完之后,會將連接對象歸還給容器 好處:…

20溫控f1什么意思_歐姆龍溫控器是什么 歐姆龍溫控器介紹【圖文】

歐姆龍溫控器&#xff0c;乍一眼看上去真的很難理解這到底是一個什么產品&#xff0c;或者是一個有什么用處的溫控器&#xff0c;對于這個比較淺顯的問題&#xff0c;不知道大家會有什么樣地感受&#xff0c;是不是正在一頭霧水的等著我進行解答呢?經過我比較淺顯的分析&#…

bootstrap 解決彈出窗口(modal) 常見問題

無法使用鍵盤esc關閉窗口方法&#xff1a; 首先在modal容器的div中增加屬性tabindex"-1"&#xff0c;其次設置鍵盤ESC屬性keyboard為true&#xff1b; 方法1&#xff1a;使用js打開窗口時 $(“#modal”).modal({keyboard:true}) 方法2&#xff1a;使用H5屬性在modal窗…

zabbix3.2學習筆記(二):服務端安裝

2019獨角獸企業重金招聘Python工程師標準>>> 一般小公司大多將zabbix web端和zabbix server部署在同一臺主機上&#xff0c;其實二者是可以分開的&#xff0c;web GUI配置連接到對應的數據庫就行&#xff0c;讓zabbix server和MySQL數據庫在同一臺主機上便于數據快速…

H.264解碼器中CAVLC碼表查找算法的分析與優化

0 引言 近年來&#xff0c;隨著信息技術飛速發展和互聯網的日益普及&#xff0c;尤其是以視頻為信息主要來源的多媒體領域越來越受到人們的關注。H&#xff0e;264是ITU-T的視頻編碼專家組(VCEG)和ISO&#xff0f;IEC的活動圖像編碼專家組(MPEG)的聯合視頻組(Joint Video Te…

python求加速度_如何利用Python 為自然語言處理加速度

自去年發布 Python 的指代消解包(coreference resolution package)之后&#xff0c;很多用戶開始用它來構建許多應用程序&#xff0c;而這些應用與我們最初的對話應用完全不同。利用 spaCy 和一點點 Cython 給 NLP 加速。自去年發布 Python 的指代消解包(coreference resolutio…

druid.properties文件的配置

# druid.properties文件的配置 driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://127.0.0.1:3306/plan usernameroot password # 初始化連接數量 initialSize5 # 最大連接數 maxActive10 # 最大超時時間 maxWait3000

jquery+easyui開發、培訓文檔

目 錄 1.... Accordion&#xff08;可折疊標簽&#xff09;....................................................................................... 2 1.1 實例.............................................................................................…

HAProxy用法詳解 全網最詳細中文文檔

一、HAProxy簡介&#xff08;1&#xff09;HAProxy 是一款提供高可用性、負載均衡以及基于TCP&#xff08;第四層&#xff09;和HTTP&#xff08;第七層&#xff09;應用的代理軟件&#xff0c;支持虛擬主機&#xff0c;它是免費、快速并且可靠的一種解決方案。 HAProxy特別適用…

mp4文件格式系列

mp4文件格式系列1 - 綜述Overview and Introduction Core Concepts MP4文件格式中&#xff0c;所有的內容存在一個稱為movie的容器中。一個movie可以由多個tracks組成。每個track就是一個隨時間變化的媒體序列&#xff0c;例如&#xff0c;視頻幀序列。track里的每個時間單…