很高興能和大家一起分享最近的學習成果,這一系列文章主要圍繞經典的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對象剖析
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,這兩個叫做門面類,相當於兩個童子,當書信要來的時候是這兩個童子負責處理,目的是爲了封裝數據。