深入學習SSH框架(Spring MVC +Spring FrameWork +Hibernate +Spring Security)《一:Servlet原理》

很高興能和大家一起分享最近的學習成果,這一系列文章主要圍繞經典的SSH框架來寫的。如有錯誤之處,敬請指正!
由於在兩年前曾經接觸過一段時間的J2EE,但那個時候只會一些簡單的配置,瞭解了一些基本的東西,大三的時候才發現這些都是皮毛,於是想深入的學習整個框架,由此寫下這一系列的分享心得,知其然更要知其所以然。


下面進入正文:

1.servlet 是什麼?

首先我們進入官方網站看看詳細信息。找到javax.servlet的包,我們發現其實servlet包裏面就是一些接口,找到servlet核心接口,裏面定義是這樣說的A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.意思是說servlet是一個運行在web服務器中的小程序,用來接收和響應來自web客戶端的請求,使用HTTP進行通信。這裏的web服務器就可以有很多了,如常見的tomcat,jetty等。

2.servlet容器又是什麼?

servlet和servlet容器是彼此依存的,但是又相互發展,servlet,jsp等都是運行在servlet容器內的,而servlet容器又是運行在web服務器的(tomcat,jetty),我們可以看看tomcat容器模型


可以看出tomcat容器分爲四個等級真正管理servlet的是context容器,其實我們可以在tomcat的目錄中tomcat/config/server.xml中發現這樣的標籤

<Context docBase="D:\Tomcat 8.0\webapps\TomcatTest" path="/TomcatTest" reloadable="true" source="org.eclipse.jst.j2ee.server:TomcatTest"/>

 <Context docBase="D:\Tomcat 8.0\webapps\GitTest" path="/GitTest" reloadable="true" source="org.eclipse.jst.j2ee.server:GitTest"/>

可以看出一個servlet容器可以有多個context,也就意味可以有多個應用,每個應用又互不干擾。

3.Servlet容器的啓動過程

我們可以看看tomcat源碼中有這樣一個啓動類,可以用java來手動啓動一個tomcat實例,並且配置相關信息,然後添加webapp應用,這一系列工作相當複雜,當好在我們可以不用這麼麻煩,直接在ide既可以啓動tomcat,tomcat容器就會按照流程對web應用進行初始化等一系列工作。

4.Web應用的初始化

web應用的初始化工作室在ContextConfig的configureStart返回中實現的,主要用來解析web.xml.也就是web程序的入口,tomcat首先會找到globalWebXml,存在於cof.web.xml中,接下來會尋找hostWebXml,然後尋找應用的配置文件web.xml,通過解析保存在WebXml對象中,最後tomcat會將WebXml對象中的屬性設置到Context容器中,這裏就包括了Servlet對象、filter過濾器、監聽器等,但是最後返回的並不是servlet對象,而是經過包裝後的ServletWrapper對象。這也就解釋了爲什麼tomcat容器模型有四層。

5.創建servlet實例

當Context容器啓動是,它會讀取conf/web.xml下的內容,這個是所有web應用程序的根,裏面定義了很多默認配置項,其實當我們的項目只有一個index.jsp沒有任何的servlet的時候,運行起來可以直接訪問index.jsp,這是因爲在默認配置項中有這樣一句話

  

<!-- ================== Built In Servlet Definitions ==================== -->
 <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>

這其中有兩個類,DefaultServlet和JspServlet,他們的載入順序分別爲1和3,意味着當tomcat啓動的時候他們就啓動了,其實這兩個類都繼承了httpservlet,而創建servlet實例是由Wrapper.loadServlet開始的,首先它會獲得servletClass,然後把它交給InstanceManager去創建基於servletClass.class的對象,如果這個對象配置了jsp-file的話,那麼這個servletClass對象就是在上面的JspServlet。

6.servlet的初始化

在StandardWrapper中有個initServlet方法,它就是來調用servlet的init方法,同時把包裝了StandardWrapper對象的StandardWrappperFacade作爲ServletConfig對象傳給servlet。如果該servlet關聯的是jsp文件,那麼初始化的就是jspServlet,接下來就會模擬以此請求,調用這個jsp,並編譯它,然後初始化這個類。

有關servlet初始化工作非常複雜,中間有非常多的過程,我們可以仔細查看源碼文檔。

7.Request和Response對象剖析

Servlet中定義了兩個對象,ServletRequest和ServletResponse對象,這兩個對象分別是HttpServletRequest和HttpServletRequest的父類,但是爲何Context容器傳過來的ServletRequest和ServletResponse對象能被轉化成

HttpServletRequest和HttpServletRequest呢?解釋如下:

當tomcat接收到一個請求時候,會創建org.apache.coyote.Request和org.apache.coyote.Response兩個類,用來描述請求和相應的信息,經過簡單處理後會分發給後續線程處理,所以他們的對象很小,容易被jvm回收,後續線程會創建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response對象,這兩個對象一直存在於整個servlet容器,直到傳給目標Servlet,但是目標接收的類型卻是RequestFacade和ResponseFacade,這兩個叫做門面類,相當於兩個童子,當書信要來的時候是這兩個童子負責處理,目的是爲了封裝數據。

8.Servlet是如何處理請求的

通常當我們發送一個請求的時候如http://localhost:8080/TomcatTest/login,容器是如何找到LoginServlet這個類來處理的呢?其實tomcat中有一個mapper類,專門用來保存tomcat中的container容器中的子容器信息,當org.apache.catalina.connector.Request類進去Container容器之前,Mapper將會根據這次請求的hostname,contextpath把這些信息設置到Request的mappingData屬性中去,最後再去匹配相應的Servlet處理器。
到此爲止Servlet已經可以幫我們完成所有工作了,但是現在的web應用很少直接將交互的全部頁面交給servlet去實現,而是採用更加高效的mvc框架去實現,這些框架的基本原理就是把所有的請求都映射到一個servlet中,然後去實現service方法,比如後面要學習的SpringMVC。

9.總結

本次學習主要回顧並深入的瞭解了Servlet,通過閱讀源碼,官方文檔讓我們對servlet有了更深層次的瞭解,因爲servlet是後續所有框架的基礎,沒有servlet後面的spring框架也還是空架子,基礎纔是重中之重。

10.附錄

有關本章的測試代碼在我的github地址源代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章