成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從Servlet講到Tomcat

Little_XM / 3302人閱讀

摘要:在組件樹中前面啟動(dòng)的過程中提到過的的線程負(fù)責(zé)接收連接,接收請求之后調(diào)用方法,把包裝成,創(chuàng)建一個(gè)任務(wù),從線程池中獲取一個(gè)線程處理該任務(wù)。

概念

Java Web,是基于Java語言實(shí)現(xiàn)web服務(wù)的技術(shù)總和。介于現(xiàn)在Java在web客戶端應(yīng)用的比較少,我把學(xué)習(xí)重點(diǎn)放在了JavaWeb服務(wù)端應(yīng)用。雖然用Springboot就可以很快地搭建一個(gè)web項(xiàng)目了,但是如果想要深入了解JavaWeb的實(shí)現(xiàn)原理,就不得不先學(xué)習(xí)Servlet和Servlet容器的相關(guān)知識(shí)。

首先什么是Servlet?

Servlet從廣義上講是Sun公司提供的一門用于開發(fā)動(dòng)態(tài)Web資源的技術(shù),而狹義上指的是實(shí)現(xiàn)了javax.servlet.Servlet接口的類的統(tǒng)稱。Servlet接口很簡單,只有init、getServletConfig、service、getServletInfo和destroy這5個(gè)方法,它們構(gòu)成了實(shí)現(xiàn)Servlet功能的規(guī)范。像Spring的DispatcherServlet,都是一種具體的Servlet。

當(dāng)然了,光有Servlet這一個(gè)類可沒什么用,它沒有main方法,不能獨(dú)立運(yùn)行,就像光有子彈沒有槍,子彈的價(jià)值就發(fā)揮不出來。要想實(shí)現(xiàn)Servlet的功能,就必須有一個(gè)Servlet容器。

Servlet容器,也叫做Servlet引擎,是web服務(wù)器的一部分,用于接收網(wǎng)絡(luò)請求,把請求轉(zhuǎn)發(fā)給對應(yīng)的Servlet,并把Servlet處理的結(jié)果返回給網(wǎng)絡(luò)。

它是web服務(wù)器和Servlet之間的媒介。

它建立服務(wù)端socket、監(jiān)聽端口、創(chuàng)建流。

它管理著Servlet的生命周期,如加載部署Servlet,實(shí)例化初始化Servlet,調(diào)用Servlet方法(處理業(yè)務(wù)),以及銷毀Servlet。

Tomcat就是一個(gè)獨(dú)立運(yùn)行的Servlet容器。

Tomcat組織結(jié)構(gòu)

下面就以Tomcat8為例,看看一個(gè)具體的Servlet容器是如何實(shí)現(xiàn)上述功能的。

先來一張Tomcat的結(jié)構(gòu)圖:

Server:Tomcat頂層容器,代表著整個(gè)服務(wù)器。包含一個(gè)或多個(gè)Service組件;

Service:存活在Server內(nèi)部的中間組件,包含Connector和Container這兩個(gè)核心組件,負(fù)責(zé)將一個(gè)或多個(gè)Connector組件綁定到一個(gè)Container上;

Connector:監(jiān)聽端口,處理與客戶端基于某種協(xié)議的通信,提供Socket與request和response的轉(zhuǎn)換;

Container:封裝和管理Servlet,負(fù)責(zé)對請求進(jìn)行處理,并生成響應(yīng)。

(先介紹這幾個(gè)大的組件,小組件在后面會(huì)細(xì)講)

這樣展示可能比較抽象,我們可以打開我們安裝的Tomcat目錄下的conf/server.xml,看下Tomcat是如何配置這些組件的:

 

     

         



         

             

                ...

             

     

     

 

先看Connector,可以看到一個(gè)Service里是可以配置多個(gè)Connector的,這里我們主要關(guān)心HTTP協(xié)議的Connector。

Connector使用持有的ProtocolHandler類型對象來處理請求,它包含的三個(gè)部件:

Endpoint:綁定端口、監(jiān)聽請求;

Processor:將Endpoint接收到的Socket封裝成Request;

Adapter:將Request交給Container進(jìn)行具體的處理。

再看Container,它內(nèi)部包含了4個(gè)子容器

Engine:Servlet引擎,Container最上層,每個(gè)Service只能包含一個(gè),表示一個(gè)特定的Service的請求處理流水線,從Connector接收處理所有的請求并返回響應(yīng);

Host:虛擬主機(jī),一個(gè)引擎可以包含多個(gè)Host;一個(gè)Host可以包含多個(gè)Context;

Context:一個(gè)Context表示了一個(gè)Web應(yīng)用程序(Web工程)。Context直接管理Servlet在容器中的包裝類;

Wrapper:每一個(gè)Servlet在容器中的包裝類。

我們可以通過Tomcat文件夾里的文件結(jié)構(gòu)來幫助理解,上面提到的conf/server.xml里配置Host時(shí)有個(gè)appBase的屬性是webapps,是不是很眼熟?我們在Tomcat安裝目錄下總是有一個(gè)webapp文件夾,整個(gè)webapps就是一個(gè)Host站點(diǎn),里面放著的每個(gè)文件夾目錄就對應(yīng)一個(gè)Context,其中ROOT目錄中存放著主應(yīng)用,其他目錄存放著子應(yīng)用。

Tomcat類加載

先看Tomcat是如何加載類的。

Tomcat啟動(dòng)時(shí)創(chuàng)建的類加載器有

BootstrapClassLoader:加載JVM提供的基本運(yùn)行類,和$JAVA_HOME/jre/lib/ext里的jar包;

SystemClassLoader:加載tomcat啟動(dòng)的類,即Catalina.bat中指定位置的類;

CommonClassLoader:加載tomcat以及應(yīng)用通用的類,位于CATALINA_HOME/lib下。父加載器是AppClassLoader;

WebAppClassLoader:每個(gè)應(yīng)用在部署后都創(chuàng)建一個(gè)唯一的類加載器,加載位于WEB-INF/lib中的jar包和WEB-INF/classes下的class文件。父加載器是CommonClassLoader。

Tomcat默認(rèn)類加載邏輯:

1、先在本地緩存中查找,如果已經(jīng)加載即返回,否則繼續(xù)下一步

2、嘗試Bootstrap加載,如果加載到即返回,否則

3、WebApp自行加載,先/WEB-INF/classes,再/WEB-INF/lib/*.jar,如果加載到即返回,否則

4、委托WebApp父類加載器(Common ClassLoader)去加載。。。

注意:第3、4兩步違反了雙親委托機(jī)制,但也只是Tomcat自定義的ClassLoader加載順序違反了,頂層還是相同的。

Tomcat的這種加載邏輯保證了每個(gè)應(yīng)用程序的同名類庫是獨(dú)立的,同時(shí)可以共享共有類庫。

每一個(gè)JSP文件對應(yīng)一個(gè)Jsp類加載器,當(dāng)一個(gè)jsp文件修改了,就直接卸載這個(gè)jsp類加載器,重新創(chuàng)建類加載器,重新加載jsp文件。

Tomcat啟動(dòng)流程

下面開始分析Tomcat大致啟動(dòng)流程,建議配合源碼食用。

Tomcat傳統(tǒng)的啟動(dòng)入口通過startup.bat和catalina.bat腳本調(diào)用org.apache.catalina.startup.Bootstrap.main(),分為兩部分:

一、init():初始化main線程的daemon(一個(gè)Bootstrap對象)。初始化Tomcat類加載器,通過反射來實(shí)例化Catalina對象;

二、daemon執(zhí)行三個(gè)方法setAwait(true)、load(args)和start():

1、setAwait:通過反射調(diào)用catalina的setAwait方法設(shè)置await屬性,后面會(huì)用到;

2、load(args):通過反射調(diào)用catalina的load方法,創(chuàng)建xml解析器,解析conf/server.xml創(chuàng)建出了StandardServer對象并init,繼而調(diào)用內(nèi)部包含的service的int,以此逐層初始化所有組件;

3、start():通過反射調(diào)用catalina的start()方法,和init方法一樣逐層start所有組件;最后利用前面設(shè)置的await屬性調(diào)用await方法,繼而調(diào)用server的await方法,保證主線程運(yùn)行并持續(xù)監(jiān)聽8005端口的SHUTDOWN指令,接收到后調(diào)用stop方法關(guān)閉Tomcat。

上面各個(gè)組件的init和start都是一筆帶過,那么他們實(shí)際完成了什么樣的工作呢?

Server.init():調(diào)用包含的Service的init;

Service.init():初始化Engine,初始化Executor(所有Connector共享的線程池),初始化mapperListener(用來保存容器映射),調(diào)用Connector.init;

Connector.init():初始化ProtocolHandler、Adapter,Endpoint創(chuàng)建ServerSocket并綁定監(jiān)聽端口

Server.start():調(diào)用包含的Services的start;

Service.start():與初始化對應(yīng),調(diào)用Engine.start,啟動(dòng)Executor,啟動(dòng)mapperListener(作為監(jiān)聽者加到容器和它們的子容器中),調(diào)用Connector.start

Connector.start():Endpoint創(chuàng)建acceptor線程來接收客戶端的連接以及poller線程來處理連接中的讀寫請求

Engine.start():逐一啟動(dòng)Host、Context、Wrapper

Context.start():步驟很多,這里列舉幾個(gè)重要的:

*)創(chuàng)建讀取資源文件的對象

*)創(chuàng)建ClassLoader對象,就是上面提到過的每個(gè)應(yīng)用唯一的WebAppClassLoader

*)設(shè)置應(yīng)用的工作目錄

*)啟動(dòng)相關(guān)輔助對象,如Logger、realm、resources等

*)通知監(jiān)聽者ContextConfig讀取和解析Web應(yīng)用web.xml和注解

*)啟動(dòng)web.xml解析到的子容器(解析時(shí)將Servlet包裝成StandardWrapper)

*)啟動(dòng)Pipeline(一種責(zé)任鏈設(shè)計(jì)模式后面會(huì)講)

*)獲取或創(chuàng)建ServletContext,并設(shè)置必要的參數(shù)

*)創(chuàng)建Context中配置的Listener;

*)創(chuàng)建和初始化配置的Filter;

*)創(chuàng)建和初始化loadOnStartup大于等于0的Servlet

Tomcat處理請求過程

現(xiàn)在我們知道Tomcat是如何啟動(dòng)的,那么啟動(dòng)之后Tomcat如何處理一次請求的呢?

下面以一次Http請求為例來說明,請求URL=http://hostname:port/contextpath/servletpath。

在Connector組件樹中:

前面啟動(dòng)的過程中提到過Connector的Endpoint的acceptor線程負(fù)責(zé)接收Socket連接,acceptor接收請求之后調(diào)用processSocket方法,把socket包裝成SocketWrapper,創(chuàng)建一個(gè)SocketProcessor任務(wù),從線程池中獲取一個(gè)線程處理該任務(wù)。run方法中調(diào)用AbstractEndpoint.Handler.process方法,根據(jù)請求的協(xié)議類型(con/server.xml中connector元素的protocol屬性值)創(chuàng)建相應(yīng)的類型處理類Processor,對SocketWrapper的輸入流和輸出流進(jìn)行包裝,根據(jù)SocketWrapper創(chuàng)建輕量級的coyote.Request和coyote.Response,解析http請求的請求頭和請求行,最后Adapter.service(Request, Response),將coyote.Request和coyote.Response轉(zhuǎn)化成Connector.Request和Connector.Response,調(diào)用connector.getService().getMapper().map(),根據(jù)hostname、contextpath和servletpath找到對應(yīng)的host、context和Wapper(前面利用mapperListener保存的容器完整關(guān)系),設(shè)置到Request中去;再調(diào)用connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)將請求傳遞給與Connector關(guān)聯(lián)的Container逐級傳遞下去(Engine->Host->Context->Wapper)。

在Container組件樹中:

Container容器按照責(zé)任鏈的設(shè)計(jì)模式,使用管道Pipeline和Value的方式來傳遞請求。

第一層是Engine,先通過conf/server.xml中配置的value,最后總會(huì)流到StandardEngineValue,調(diào)用host.getPipeline().getFirst().invoke(request, response)將請求傳遞給request中保存的Host;

第二層是Host,同樣先通過配置的value,最后流到StandardHostValue,再傳遞給request中保存的Context;

第三層是Context,流到StandardContextValue傳遞給request中保存的Wapper;

最后是Wapper,流到StandardWapperValue,獲取Servlet單例(雙檢查鎖機(jī)制),獲取FilterChain執(zhí)行Filter鏈,也是一種責(zé)任鏈模式,執(zhí)行完所有配置的Filter后執(zhí)行Servlet.service,即我們希望其完成的業(yè)務(wù)邏輯。

(在進(jìn)入Filter的時(shí)候,傳入的是Connector.Request的門面類RequestFacade,和Request一樣都是HttpServletRequest和HttpServletResponse的實(shí)現(xiàn)類)

返回過程略。

第一次寫文章,條理排版不是很清晰,以后慢慢改進(jìn)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72314.html

相關(guān)文章

  • Servlet第四篇【request對象常用方法、應(yīng)用】

    摘要:瀏覽器的中文數(shù)據(jù)提交給服務(wù)器,以編碼對中文編碼,當(dāng)我在讀取數(shù)據(jù)的時(shí)候,拿到的當(dāng)然是亂碼。接下來使用方式傳遞中文數(shù)據(jù),把表單的方式改成即可當(dāng)我們訪問的時(shí)候,又出現(xiàn)亂碼了于是我按照上面的方式,把對象設(shè)置編碼為試試結(jié)果還是亂碼。 什么是HttpServletRequest HttpServletRequest對象代表客戶端的請求,當(dāng)客戶端通過HTTP協(xié)議訪問服務(wù)器時(shí),HTTP請求頭中的所有信...

    raise_yang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<