servlet接口的方法,第九章:Servlet工作原理解析

 2023-12-25 阅读 32 评论 0

摘要:9.1 從Servlet容器說起 Servlet和Servlet容器的關系,就像槍和子彈的關系,彼此依存又互相獨立發展,這一切都是為了適應工業化生產。從技術角度來說,是為了解耦,通過標準化接口來互相協作。 所以說,接口是連接Servlet和Servlet容器的

9.1 從Servlet容器說起

Servlet和Servlet容器的關系,就像槍和子彈的關系,彼此依存又互相獨立發展,這一切都是為了適應工業化生產。從技術角度來說,是為了解耦,通過標準化接口來互相協作。

所以說,接口是連接Servlet和Servlet容器的關鍵。那么我們就從它們的接口說起。

以Tomcat為例:

servlet接口的方法?下圖為Tomcat的容器等級:

Wrapper為Servlet在容器中的包裝類。Context直接管理包裝類Wrapper.

上圖可以看出,Tomcat容器分為4個等級(Container容器,Engine,Host,Context),真正管理Servlet的容器是Context容器。一個Context對應一個web工程。

servlet原理,Servlet容器的啟動過程

選擇的是Tomcat7自帶的examples web工程。(書中代碼)

創建了一個Tomcat實例并新增了一個web應用,然后啟動Tomcat并調用其中一個HelloWorldExample Servlet。

Tomcat tomcat = getTomcatInstance();
File appDir =  new File(getBuilderDirectory(),"webapps/examples");
tomcat.addWebapp(null,"/examples",appDir.getAbsolutePath());
tomcate.start();
ByteChunk res = getUrl("http://localhost:"+getPort()+"/examples/servlets/servlet/HelloWorldExample");
assertTrue(res.toString().indexOf("<h1>Hello </h1>")>0);      

關于以上代碼中的addWebapp{

在路徑為的上下文中、  創建一個StandardContext容器,并且給這個Context容器設置必要的參數(url,path)。url和path分別代表這個應用在Tomcat中的訪問路徑和這個應用的實際物理路徑。

  創建ContextConfig,用Context來接收(多態),這個類會負責整個web應用配置的解析工作。

  然后將ContextConfig添加到StandardContext的LifecycleListener中。

  配置完后將Context容器加到父容器Host中。

servlet?}

接下來調用Tomcat的start方法啟動Tomcat。

Tomcat的啟動邏輯是基于觀察者模式設計的。所有的容器都會繼承LifecycleListener接口。它管理著容器的整個生命周期。所有容器的修改和狀態的改變都會由它去通知已經注冊的觀察者(listener)

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

  創建用于解析XML配置文件的contextDigester對象。

  讀取默認的context.xml配置文件,如果存在則解析它。

  讀取默認的Host的文件,如果存在則解析它

  讀取默認的Context自身的配置文件,如果存在則解析它

  設置Context的DocBase。

ContextConfig的init方法完成以后,Context容器就會執行startInternal方法,主要包括如下:

  創建讀取資源文件的對象

  創建ClassLoader對象

  設置應用的工作目錄

  啟動相關的輔助類,如logger,realm,resources等

  修改啟動狀態,通知感興趣的觀察者

  子容器的初始化

  獲取ServletContext并設置必要的參數

  初始化"load on startup"的Servlet

web應用的初始化工作:

web應用的初始化工作是在ContextConfig的configureStart方法中實現的,應用的初始化主要是解析web.xml文件。

Tomcat →globalWebXml→hostWebXml→examples/WEB-INF/web.xml。? (web.xml文件的各個配置項將會被解析成相應的屬性保存在WebXml對象中)

如果當前應用支持servlet 3.0,還會完成額外9項工作。

接下來將WebXml對象中的屬性設置到Context容器中。這段代碼在WebXml的configureContext方法中。(這段代碼將Servlet包裝成了StandardWrapper并添加在了Context容器中)

把Servlet包裝成StanderdWrapper的原因:

  這里的StanderWrapper是Tomcat容器的一部分,它具有容器的特征,而Servlet作為一個獨立的Web開發標準,不應該強耦合在Tomcat中。

9.2 創建Servlet實例

如果Servlet的load-on-startup配置項大于0,那么在Context容器啟動時就會被實例化。

conf/web.xml中定義了兩個Servlet,分別是DefaultServlet和JspServlet,他們的load-on-startup分別是1和3。當tomcat啟動時,他們兩個都會啟動。

創建Servlet實例的方法是從Wrapper.loadServlet開始的

Wrapper.loadServlet (獲取servletClass)→交給InstanceManager→創建一個基于servletClass.class的對象。

初始化Servlet

StandardWrapper的initServlet方法{

  調用Servlet的init方法

  將StanderdWrapperFacade作為ServletConfig傳給Servlet。

}

9.3 Servlet體系結構

與Servlet主動關聯的三個類:ServletConfig???? ServletRequest????? ServletResponse

ServletConfig在Servlet初始化的時候就傳給Servlet了。

  ServletConfig里面的方法可以獲取一些Servlet的配置屬性。

后兩個是在請求達到時調用Servlet傳遞過來的。

?

  Servlet的運行模式是一個典型的握手型交互模式。就是說,兩個模塊為了交換數據通常都會準備一個交易場景,這個場景一直跟隨這個交易過程直到這個交易完成為止。交易場景的初始化是根據這次交易對象指定的參數來定制的。

  交易場景就由ServletContext來描述,定制的參數集合就由ServletConfig來描述。

  ServletRequest和ServletResponse通常作為運輸工具來傳遞交互結果。

?

init時,StanderdWrapperFacade作為ServletConfig傳給Servlet,那么ServletConfig到底是個什么對象呢?

  StanderdWrapper和StandardWrapperFacade都實現了ServletConfig接口。而StandardWrapperFacade是StandardWrapper的門面類。這個類傳給Servlet的作用是保證從StandardwWrapper中拿到的ServlertConfig所規定的的數據,而又不把ServletConfig不關心的數據暴露給Servlet。

  同理:在Servlet中拿到的ServletContext的實際對象也是ApplicationContextFacade對象。作用和上面相同。

  同理:在Servlet中拿到的Request和Response類也是門面類RequestFacade和ResponseFacade。(這里多了幾步操作)

  這里使用的都是門面設計模式

?

9.4 Servlet如何工作

  用戶從瀏覽器向服務器發起的一個請求通常會包含如下信息:http://hostname:port/contextpath/servletpath?? ,hostname和port用來與服務器建立TCP連接。后面的URL才用來選擇在服務器中哪個子容器服務用戶的請求。那么服務器是如何根據這個URL來到達正確的Servlet容器呢?

  在Tomcat 7 中很容易解決,這種映射專門交給了一個叫Mapper的類來完成。這個類保存了Tomcat類的Container容器中的所有子容器信息。在Request類進入Container容器之前,Mapper將會根據這次請求的hostname和contextpath將Host和Context容器設置到Request?的MappingData屬性中。所以,在Request進入Container容器之前,對于它要訪問哪個子容器就已經確定了。

  那么,Mapper中是怎么擁有容器的完整關系的?

  MapperListener的init()方法{

    將MapperListener類作為一個監聽者加到整個Container容器的每個子容器中。這樣只要任何一個容器發生變化,MapperListener都會被通知到。

    同時,MapperListener的Mapper屬性也會被更改。

  }

Request請求是如何到達最終的Wrapper容器的呢(查資料 結合書? 再來填坑)

  接下來,該執行Servlet的service方法了。

  通常情況下,我們自己定義的Servlet并不直接去實現Servlet接口,而是去繼承更簡單的HttpServlet類或者GenericServlet類,我們可以有選擇的覆蓋相應的方法完成要實現的工作。

  現在的web應用很少直接將交互的全部頁面用Servlet實現,而是采用更加高效的MVC框架來實現。這些MVC框架的基本原理是將所有的請求都映射到一個Servlet,然后去實現service方法。這個方法也就是MVC框架的入口。這個博客講的很好→

  當Servlet從Servlet容器中移除時,也就表明Servlet的生命周期結束了。destory()方法開始被調用。

9.5 Servlet中的Listener

  Listener是基于觀察者模式設計的

  能夠方便地從另一個縱向維度控制程序和數據。目前Servlet提供了6種兩類事件的觀察者接口。

事實上,這6個Listener都繼承了EventListener接口。

注意:ServletContextListener在容器啟動后就不能再添加新的了,因為它所監聽的事件已經不會再出現了。

9.6 Filter如何工作

使用:通過<filter>和<filter-mapping>組合使用Filter。

Filter除了提供request和response對象外,還提供了一個FilterChain對象。

而Filter的實現類,是用戶自定義的。只需要實現Filter接口中定義的三個接口就行。

  init(FilterConfig):初始化接口。與Servlet中的init(ServletConfig)基本一樣。可以獲取到ServletContext對象,還能獲取到在<filter>下配置的<init-param>參數值

  doFilter(ServletRequest,ServletResponse,FilterChain)每個用戶請求進來的時都會被調用,在service方法之前被調用。FilterChain代表當前的整個請求鏈。這里是責任鏈設計模式

  destory:Filter容器被銷毀時調用。當web容器調用這個方法之后,還會再調用一次doFilter方法。

FilterChain的實現類ApplicationFilterChain里面有一個filters數組,保存了到達最終Servlet對象的所有Filter對象,每執行一個Filter對象,數組當前的計數都會加1。計數等于數組長度時,Servlet執行。

9.7 Servlet中的url-pattern

  Servlet和Filter在web.xml中都有<url-pattern>這個配置項。

  Servlet是通過Mapper類完成的,它在一個請求被創建時就已經匹配了。

  Filter的<url-pattern>是在創建ApplicationFilterChain對象時進行的。它會把所有的定義的Filter的url-pattern與當前的url匹配。匹配成功就將這個Filter保存到filters數組里,然后在FilterChain中調用。

?

  <url-pattern>的解析規則:精確匹配,路徑匹配(前綴匹配),后綴匹配。

  web.xml加載時,會首先檢查<url-pattern>配置是否符合規則。在StandardContext的validateURLPattern方法中檢查的。不成功會報錯。

  

  Servlet的匹配規則:精確匹配>最長路徑匹配>后綴匹配。 注:一次請求只會成功匹配到一個Servlet。

  Filter的匹配規則:只要匹配成功,都會在請求鏈被調用。<url-pattern>不能用以“/”開頭并且以“/*”結尾的根據路徑映射,和以"*."開頭的作為根據擴展名映射。→

轉載于:https://www.cnblogs.com/tonbby/p/9067702.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/194961.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息