日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

servlet工作原理之tomcat篇

發(fā)布時間:2023/12/14 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 servlet工作原理之tomcat篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

servlet容器

servlet容器的啟動過程

web應(yīng)用的初始化

創(chuàng)建servlet實(shí)例

創(chuàng)建servlet對象

初始化servlet

servlet體系結(jié)構(gòu)

ServletConfig

ServletRequest和ServletResponse

servlet如何工作

Session與cookie

servlet中的listener


本文概要見另一篇文章:https://blog.csdn.net/java_2017_csdn/article/details/78248127

servlet容器

servlet容器作為一個獨(dú)立發(fā)展的標(biāo)準(zhǔn)化產(chǎn)品,種類很多。例如jetty,在定制化和移動領(lǐng)域有不錯的發(fā)展,我這里以tomcat為例介紹servlet容器如何管理servlet。tomcat容器等級中,context容器是直接管理servlet在容器中的包裝類wrapper,所以context容器如何運(yùn)行將直接影響servlet的工作方式。

圖1.?tomcat容器模型

?

servlet容器的啟動過程

一個web應(yīng)用對應(yīng)一個context容器,添加一個應(yīng)用時將會創(chuàng)建一個StandardContext容器,并且給這個context容器設(shè)置必要的參數(shù),url和path分別代表這個應(yīng)用在tomcat中的訪問路徑和這個應(yīng)用實(shí)際的物理路徑。其中最重要的一個配置是ContextConfig,這個類將會負(fù)責(zé)整個web應(yīng)用配置的解析工作,最后將這個context容器加到父容器host中。

接下來會調(diào)用tomcat的start方法啟動tomcat。tomcat的啟動邏輯是基于觀察者模式設(shè)計的,所有的容器都會繼承l(wèi)ifecycle接口,它管理著容器的整個生命周期,所有容器的修改和狀態(tài)改變都會由它去通知已經(jīng)注冊的觀察者。

圖2 tomcat主要類的啟動時序圖:

當(dāng)context容器初始化狀態(tài)為init時,添加在context容器的listener將會被調(diào)用。ContextConfig繼承了LifecycleListener接口,它是在調(diào)用清單3時被加入到StandardContext容器中的。ContextConfig類會負(fù)責(zé)整個web應(yīng)用的配置文件解析工作。

ContextConfig的init方法主要完成以下工作:

  • 創(chuàng)建用于解析xml配置文件的contextDigester對象
  • 讀取默認(rèn)context.xml配置文件,解析
  • 讀取默認(rèn)host配置文件,解析
  • 讀取默認(rèn)context配置文件,解析
  • 設(shè)置context的docBase
  • ContextConfig的init方法完成后,context容器會執(zhí)行startInternal方法,這個方法啟動邏輯比較復(fù)雜,主要包括如下幾個部分:

  • 創(chuàng)建讀取資源文件的對象
  • 創(chuàng)建ClassLoader對象
  • 設(shè)置應(yīng)用的工作目錄
  • 啟動相關(guān)的輔助類如:logger realm resources等
  • 修改啟動狀態(tài),通知感興趣的觀察者(web應(yīng)用的配置)
  • 子容器的初始化
  • 獲取ServletContext并設(shè)置必要的參數(shù)
  • 初始化"load on startup"的servlet
  • web應(yīng)用的初始化

    web應(yīng)用的初始化是在ContextConfig的configureStart方法中實(shí)現(xiàn)的,應(yīng)用的初始化主要是要解析web.xml文件,這個文件描述了一個web應(yīng)用的關(guān)鍵信息,也是一個web應(yīng)用的入口。

    tomcat首先會找globalWebXml這個文件的搜索路徑,是在engine的工作目錄下尋找以下兩個文件中的任一個:org/apache/catalina/startup/NO_DEFAULT_XML或conf/web.xml。接著會找hostWebXml這個文件,可能會在System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default,接著尋找應(yīng)用的配置文件examples/WEB-INF/web.xml。web.xml文件中的各個配置項(xiàng)將會被解析成相應(yīng)的屬性,保存在WebXml對象中。

    如果當(dāng)前應(yīng)用支持servlet3.0,解析還將完成額外9項(xiàng)工作,這個額外的9項(xiàng)工作主要是為servlet3.0新增的特性,包括jar包中的META-INF/web-fragment.xml的解析以及對annotations的支持。

    接下去會將webxml對象中的屬性設(shè)置到context容器中,這里包括創(chuàng)建servlet對象、filter、listener等。這段代碼在webxml的configureContext方法中。下面是解析servlet的代碼片段:

    清單4.創(chuàng)建wrapper實(shí)例

    for (ServletDef servlet : servlets.values()) { Wrapper wrapper = context.createWrapper(); String jspFile = servlet.getJspFile(); if (jspFile != null) { wrapper.setJspFile(jspFile); } if (servlet.getLoadOnStartup() != null) { wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue()); } if (servlet.getEnabled() != null) { wrapper.setEnabled(servlet.getEnabled().booleanValue()); } wrapper.setName(servlet.getServletName()); Map<String,String> params = servlet.getParameterMap(); for (Entry<String, String> entry : params.entrySet()) { wrapper.addInitParameter(entry.getKey(), entry.getValue()); } wrapper.setRunAs(servlet.getRunAs()); Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs(); for (SecurityRoleRef roleRef : roleRefs) { wrapper.addSecurityReference( roleRef.getName(), roleRef.getLink()); } wrapper.setServletClass(servlet.getServletClass()); MultipartDef multipartdef = servlet.getMultipartDef(); if (multipartdef != null) { if (multipartdef.getMaxFileSize() != null && multipartdef.getMaxRequestSize()!= null && multipartdef.getFileSizeThreshold() != null) { wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation(), Long.parseLong(multipartdef.getMaxFileSize()), Long.parseLong(multipartdef.getMaxRequestSize()), Integer.parseInt( multipartdef.getFileSizeThreshold()))); } else { wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation())); } } if (servlet.getAsyncSupported() != null) { wrapper.setAsyncSupported( servlet.getAsyncSupported().booleanValue()); } context.addChild(wrapper); }

    這段代碼清楚地描述了如何將servlet包裝成context容器中的StandardWrapper,這里有個疑問,為什么要將servlet包裝成StandardWrapper。這里StandardWrapper是tomcat容器中的一部分,它具有容器的特征;而servlet是一個獨(dú)立的web開發(fā)標(biāo)準(zhǔn),不應(yīng)該強(qiáng)耦合在tomcat中。

    除了將servlet包裝成StandardWrapper并作為子容器添加到context中,其他的所有web.xml屬性都被解析到context中,所以說context容器才是真正運(yùn)行servlet的容器。一個web應(yīng)用對應(yīng)一個context容器,容器的配置屬性由應(yīng)用的web.xml指定。

    ?

    創(chuàng)建servlet實(shí)例

    前面已經(jīng)完成了servlet的解析工作,并且被包裝成StandardWrapper添加在Context容器中,但是它仍然不能為我們工作,它還沒有被實(shí)例化。下面我們將介紹servlet對象是如何創(chuàng)建以及初始化的。

    創(chuàng)建servlet對象

    如果servlet的load-on-startup配置項(xiàng)大于0,那么在context容器啟動的時候就會被實(shí)例化,前面提到在解析配置文件時會讀取默認(rèn)的globalWebXml,在conf的web.xml文件中定義了一些默認(rèn)的配置項(xiàng),其定義了兩個servlet,分別是:org.apache.catalina.servlets.DefaultServlet和org.apache.jasper.servlet.JspServlet。它們的load-on-startup分別是1和3,也就是當(dāng)tomcat啟動時這兩個servlet就會被啟動。

    創(chuàng)建servlet實(shí)例的方法是從Wrapper.loadServlet開始的。loadServlet方法要完成的就是獲取servletClass然后把它交給InstanceManager去創(chuàng)建一個基于servletClass.class的對象。如果這個servlet配置了jsp-file,那么這個servletClass就是conf/web.xml中定義的org.apache.jasper.servlet.JspServlet了。

    圖3. 創(chuàng)建Servlet對象的相關(guān)類結(jié)構(gòu)

    初始化servlet

    初始化servlet在StandardWrapper的initServlet方法中,這個方法很簡單,就是調(diào)用servlet的init方法,同時把包裝了StandardWrapper對象的StandardWrapperFacade作為ServletConfig傳給servlet。

    如果該servlet關(guān)聯(lián)的是一個jsp文件,那么前面初始化的就是JspServlet,接下去會模擬一次簡單請求,請求調(diào)用這個jsp文件,以便編譯這個jsp文件為class,并初始化這個class。

    這樣servlet對象就初始化完成了,事實(shí)上servlet從被web.xml中解析到完成初始化,這個過程非常復(fù)雜,中間有很多過程,包括各種容器狀態(tài)的轉(zhuǎn)化引起的監(jiān)聽事件的觸發(fā)、各種訪問權(quán)限的控制和一些不可預(yù)料的錯誤發(fā)生的判斷行為等等。

    圖4. 初始化servlet的時序圖

    servlet體系結(jié)構(gòu)

    我們知道Java web 應(yīng)用是基于servlet規(guī)范運(yùn)轉(zhuǎn)的,那么sevlet本身又是如何運(yùn)轉(zhuǎn)的呢?為何要設(shè)計這樣的體系結(jié)構(gòu)。

    圖5. servlet 頂層類關(guān)聯(lián)圖

    從上圖可以看出servlet規(guī)范就是基于這幾個類運(yùn)轉(zhuǎn)的,與servlet主動關(guān)聯(lián)的是三個類,分別是ServletConfig、ServletRequest和ServletResponse。這三個類都是通過容器傳給servlet的,其中ServletConfig是在servlet初始化時就傳給servlet了,而后兩個是在請求達(dá)到時調(diào)用servlet時傳遞過來的。

    ServletConfig

    仔細(xì)看servletConfig接口中聲明的方法,可以發(fā)現(xiàn)這些方法都是為了獲取這個servlet的一些配置屬性,而這些配置屬性可能在servlet運(yùn)行時被用到。而ServletContext又是干什么的呢?

    servlet的運(yùn)行模式是一個典型的“握手型的交互式”運(yùn)行模式。所謂“握手型的交互式”就是兩個模塊為了交換數(shù)據(jù)通常都會準(zhǔn)備一個交易場景,這個場景一直跟隨這個交易過程直到交易完成為止。這個交易場景的初始化是根據(jù)這次交易對象指定的參數(shù)來定制的,這些指定參數(shù)通常就會是一個配置類。對號入座,交易場景就由ServletContext來描述,而定制的參數(shù)集合就由ServletConfig來描述。ServletConfig是在Servlet.init時由容器傳過來的。

    圖6. ServletConfig在容器中的類關(guān)聯(lián)圖

    上圖可以看出StandardWrapper和StandardWrapperFacade都實(shí)現(xiàn)了ServletConfig接口,而StandardWrapperFacade是StandardWrapper門面類。所以傳給servlet的是StandardWrapperFacade對象,這個類能夠保證從StandardWrapper中拿到ServletConfig所規(guī)定的數(shù)據(jù),而又不把ServletConfig不關(guān)心的數(shù)據(jù)暴露給servlet。

    同樣ServletContext也與ServletConfig有類似的結(jié)構(gòu),Servlet中能拿到的ServletContext的實(shí)際對象也是ApplicationContextFacade對象。ApplicationContextFacade同樣保證ServletContext只能從容器中拿到它該拿的數(shù)據(jù),它們都起到對數(shù)據(jù)的封裝作用,它們使用的都是門面設(shè)計模式。通過ServletContext可以拿到Context容器中一些必要信息,比如應(yīng)用的工作路徑,容器支持的Servlet最小版本等。

    ServletRequest和ServletResponse

    我們在創(chuàng)建自己的Servlet類時通常使用的都是HttpServletRequest和HttpServletResponse,它們繼承了ServletRequest和ServletResponse。

    圖7. request相關(guān)類結(jié)構(gòu)圖

    上圖是tomcat創(chuàng)建的request和response的類結(jié)構(gòu)圖。tomcat一接受到請求首先會創(chuàng)建org.apache.coyote.Request和org.apache.coyote.Response,這兩個類是Tomcat內(nèi)部使用的描述一次請求和相應(yīng)信息的類,它們是一個輕量級的類,作用就是在服務(wù)器接收到請求后,經(jīng)過簡單解析將這個請求快速地分配給后續(xù)線程去處理。

    接下去當(dāng)交給一個用戶線程去處理這個請求時又創(chuàng)建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response對象。這兩個對象一直穿越整個Servlet容器直到要傳給Servlet,傳給servlet的是request和response的門面類RequestFacade和ResponseFacade,這里使用門面模式與前面一樣都是基于同樣的目的——封裝容器中的數(shù)據(jù)。

    圖8.一次請求中request和response的轉(zhuǎn)變過程

    servlet如何工作

    當(dāng)用戶從瀏覽器向服務(wù)器發(fā)起一個請求,通常會包含如下信息:http://hostname:port/contextpath/servletpath,hostname和port是用來與服務(wù)器建立tcp連接,而后面的url才是用來選擇服務(wù)器中哪個子容器服務(wù)用戶的請求。

    那服務(wù)器是如何根據(jù)這個url達(dá)到正確的servlet容器中的呢?這種映射工作有專門一個類來完成,這個就是org.apache.tomcat.util.http.mapper,這個類保存了Tomcat的Container容器中的所有子容器的信息。

    當(dāng)org.apache.catalina.connector.Request類在進(jìn)入container容器之前,mapper會根據(jù)這次請求的hostname和contextPath,將host和context容器設(shè)置到request的mappingData屬性中。

    圖9. request的mapper類關(guān)系圖

    圖10. request在容器中的路由圖

    上圖描述了一次request請求是如何達(dá)到最終的wrapper容器的,請求到達(dá)最終的servlet還要完成一些步驟,必須要執(zhí)行filter鏈、以及要通知你在web.xml中定義的listener。接下去就要執(zhí)行servlet的service方法。

    當(dāng)servlet從容器中移除時,也就表明servlet的生命周期結(jié)束了,這時servlet的destroy方法將被調(diào)用。

    Session與cookie

    servlet能夠給我們提供兩部分?jǐn)?shù)據(jù),一個是在servlet初始化時調(diào)用init方法時設(shè)置的ServletConfig,這個類基本上含有了servlet本身和servlet所運(yùn)行的容器的基本信息。還有一部分是由ServiceRequest類提供,它的實(shí)際對象是RequestFacade,從提供的方法中發(fā)現(xiàn)主要是描述這次請求的http協(xié)議的信息。

    session與cookie的作用都是為了保持訪問用戶與后端服務(wù)器的交互狀態(tài)。服務(wù)器通過session id創(chuàng)建HttpSession對象,第一次觸發(fā)是通過request.getSession方法,如果當(dāng)前的session id還沒有對應(yīng)的HttpSession對象那就創(chuàng)建一個新的,并將這個對象加到org.apache.catalina.Manager的session容器中保存,Manager類將管理所有Session的生命周期,Session過期將被回收,服務(wù)器關(guān)閉,Session將被序列化到磁盤。只要這個HttpSession對象存在,用戶就可以根據(jù)session id來獲取到這個對象,也就達(dá)到了狀態(tài)的保持。

    圖11. Session相關(guān)類圖

    從上圖可以看出request.getSession中獲取的HttpSession對象是StandardSession對象的門面對象,這與前面的Request和Servlet是一樣的原理。

    圖12. Session工作的時序圖

    servlet中的listener

    listener的設(shè)計對開發(fā)servlet應(yīng)用程序提供了一種快捷的手段,能夠方便地從另一個縱向維度控制程序和數(shù)據(jù)。目前servlet中提供了5種兩類事件的觀察者接口,它們分別是:4個EventListeners類型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener;和2個LifecycleListener類型的,ServlectContextListener、HttpSessionListener。

    圖13. servlet中的listener

    這些listener的實(shí)現(xiàn)類可以配置在web.xml中的<listener>標(biāo)簽中,也可以在應(yīng)用程序中動態(tài)添加。

    總結(jié)

    以上是生活随笔為你收集整理的servlet工作原理之tomcat篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。