Servlet規範簡介

引言

Web 框架一般是通過一個 Servlet 提供統一的請求入口,將指定的資源映射到這個 servlet, 在這個 servlet 中進行框架的初始化配置,訪問 Web 頁面中的數據,進行邏輯處理後,將結果數據與的表現層相融合並展現給用戶。 WEB 框架想要在符合 Servlet 規範的容器中運行,同樣也要符合 Servlet 規範。

將一個 WEB 框架注入到一個 servlet 中,主要涉及到 Servlet 規範中以下部分:

Ø         部署描述符

Ø         映射請求到 Servlet

Ø         Servlet 生存週期

Ø         請求分發

 Servlet 相關技術規範簡介

部署描述符

部署描述符就是位於 WEB 應用程序的 /WEB-INF 目錄下的 web.xml 的 XML 文件,是 WEB 應用程序不可分割的部分,管理着 WEB 應用程序的配置。部署描述符在應用程序開發人員,應用程序組裝人員,應用程序部署人員之間傳遞 WEB 應用程序的元素和配置信息。

在 WEB 應用程序的部署描描述符中以下類型的配置和部署信息是所有的 servlet 容器必須支持的:

Ø         ServletContext 初始化參數

Ø         Session 配置

Ø        Servlet 聲明

Ø        Servlet 映射

Ø        應用程序生存週期監聽器

Ø         Filter 的定義和映射

Ø         MIME 類型的映射

Ø        歡迎文件列表

Ø        錯誤文件列表

出現在部署描述符中的安全信息可以不被支持,除非這個 Servlet 容器是 J2EE 規範實現的一部分。

所有正確的 WEB 應用程序部署描述符 (Servlet2.3 規範 ) 必須包含下面的 DOCTYPE 聲明:

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web

Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

 

下面說明在部署描述符中是如何進行 Servlet 聲明和映射的,這個 DTD 的全部內容可以在下面這個地址獲得:

http://java.sun.com/dtd/web-app_2_3.dtd

在這個 DTD 中有關 Servlet 聲明和映射和映射的部分如下:

<!--

The servlet element contains the declarative data of a

servlet. If a jsp-file is specified and the load-on-startup element

is present, then the JSP should be precompiled and loaded.

Used in: web-app

-->

<!ELEMENT servlet (icon?, servlet-name, display-name?, description?,

(servlet-class|jsp-file), init-param*, load-on-startup?, runas?,

security-role-ref*)>

<!--

The servlet-class element contains the fully qualified class name

of the servlet.

Used in: servlet

-->

<!ELEMENT servlet-class (#PCDATA)>

<!--

The servlet-mapping element defines a mapping between a servlet

and a url pattern

Used in: web-app

-->

<!ELEMENT servlet-mapping (servlet-name, url-pattern)>

<!--

The servlet-name element contains the canonical name of the

servlet. Each servlet name is unique within the web application.

Used in: filter-mapping, servlet, servlet-mapping

-->

<!ELEMENT servlet-name (#PCDATA)>

根據以上 DTD ,一個典型的 Servlet 的聲明的格式如下:

<servlet>

<servlet-name>catalog</servlet-name>

<servlet-class>com.mycorp.CatalogServlet</servlet-class>

<init-param>

<param-name>catalog</param-name>

<param-value>Spring</param-value>

</init-param>

</servlet>

一個典型的Servlet映射如下:

<servlet-mapping>

<servlet-name>catalog</servlet-name>

<url-pattern>/catalog/*</url-pattern>

</servlet-mapping>

 

通過上面的方法,我們就聲明瞭一個名稱爲 catalog 的Servlet,它的實現類爲com.mycorp.CatalogServlet,並且帶有一個catalog參數,參數值爲Spring,所有向/catalog/*的請求都被映射到名稱爲catalog的Servlet。

 

映射請求到 Servlet

接 收到一個請求後,WEB容器要確定轉到哪一個WEB應用程序。被選擇的應用程序的最長的上下文路徑必須和請求的URL開始部分匹配。URL匹配的部分是映射到Servlet的上下文路徑。

WEB 容器下一步必須按照下面的程序定位處理請求的Servlet。

用來映射到Servlet的路徑是請求對象的URL減去上下文的路徑。下面的URL路徑映射規則按順序執行,容器選擇第一個成功的匹配並且不在進行下一個匹配:

Ø        容器試着對請求的路徑和Servlet的路徑進行精確匹配,如果匹配成功則選擇這個Servlet。

Ø        容器會循環的去試着匹配最長的路徑前綴:把’/’當作路徑分隔符,按照路徑樹逐級遞減的完成,選擇最長匹配的Servlet。

Ø        如果這個URL路徑的最後有擴展名(比如.jsp),Servlet容器會試着匹配處理這個擴展名的Servlet。

Ø        如果前面的沒有與前面三條規則相匹配的Servlet,容器會試着爲資源請求提供適當的資源,如果有“默認”的Servlet定義給這個應用程序,那麼這個Servlet會被使用。

 

容器必須使用一個大小寫敏感的匹配方式。

在部署描述符中,用下面的語法定義映射:

Ø        一個以’/’開始並且以’/*’結束的字符串用來映射路徑。

Ø        一個以’*.’爲前綴的字符串用來映射擴展名。

Ø        一個只包含’/’的字符串指示着這個應用程序“默認”的Servlet,在這種情況下,servlet的路徑是請求的URI減去上下文路徑,並且這個路徑是null。

Ø        所有其他的字符只用來精確匹配。

如果容器內置JSP容器,那麼*.jsp被映射到這個容器,並允許JSP頁面在需要的時候被執行。這種映射叫做隱含映射。如果WEB應用程序中定義了*.jsp的映射,那麼這個映射有比隱含映射高的優先級。

WEB 容器允許顯式的聲明隱含映射以獲得優先級,例如,*.shtml的隱含映射可以在服務器上被映射爲包含功能。

映射實例:

path pattern

servlet

/foo/bar/*

servlet1

/baz/*

servlet2

/catalog

servlet3

*.bop

servlet4

下面是實際請求映射的結果

incoming path

servlet handling request

/foo/bar/index.html

servlet1

/foo/bar/index.bop

servlet1

/baz

servlet2

/baz/index.html

servlet2

/catalog

servlet3

/catalog/index.html

“default” servlet

/catalog/racecar.bop

servlet4

/index.bop

servlet4

請注意/catalog/index.html 和/catalog/racecar.bop這兩種情況,因爲是精確匹配,所以並沒有映射到處理/catalog的servlet。

 

Servlet 生存週期

在介紹 Servlet 的生存週期之前需要先介紹一下 javax.servlet.Servlet 接口。所有的 Servlet 必須實現或者間接實現這個藉口,我們通常可以通過繼承 javax.servlet.GenericServlet 或者 javax.servlet.http.HttpServlet. 類來實現這個接口。

這個接口中定義了下面 5 種方法:

public void init(ServletConfig config);

public ServletConfig getServletConfig();

public void service(ServletRequest req, ServletResponse res);

public String getServletInfo();

public void destroy() ;

 

init() 方法

init 方法在容器器裝入 Servlet 時執行, Servlet 容器在實例化後只調用一次 init 方法, init 方法必須在 servlet 接收到任何請求之前完成。

這個方法通常用來進行一些資源的管理和初始化,如從配置文件讀取配置數據,讀取初始化參數,初始化緩衝遲等一次性的操作。

getServletConfig() 方法

GetServletConfig 方法返回一個 ServletConfig 對象,該對象用來返回這個 Servlet 的初始化信息和啓動參數。返回的是傳遞到 init 方法 ServletConfig 。

Service() 方法

Service 方法是應用程序邏輯的進入點,是 servlet 方法的核心, WEB 容器調用這個方法來響應進入的請求,只有 servlet 成功被 init() 方法初始化後, Service 方法纔會被調用。

getServletInfo() 方法

這個方法返回一個字符串對象,提供有關 servlet 的信息,如作者、版本等。

destroy() 方法

destroy 方法在容器移除 Servlet 時執行,同樣只執行一次。這個方法會在所有的線程的 service() 方法執行完成或者超時後執行,調用這個方法後,容器不會再調用這個 servlet 的方法,也就是說容器不再把請求發送給這個 Servlet 。       這個方法給 servlet 釋放佔用的資源的機會,通常用來執行一些清理任務。

 

這個接口定義了初始化一個 servlet, 服務請求和從容器中移除 servlet 的方法。他們按照下面的順序執行:

1.         servlet 被實例化後,用 init 方法進行初始化

2.         客戶端的任何請求都調用 service 方法

3.         servlet 被移除服務,調用 destroy 方法銷燬

servlet 的生存週期如下圖:
 

請求分發

請求分發可以讓一個Servlet把請求分配到另外一個資源,RequestDispatcher接口提供了實現他的機制。可以通過下面兩種方式從ServletContext中獲得一個實現了RequestDispatcher接口的對象:

• getRequestDispatcher

• getNamedDispatcher

getRequestDispatcher方法接受一個指向目標資源的URL路徑

RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”);

 

 

getNamedDispatcher方法接受一個Servlet名稱參數,這個名稱是在部署描述符中<servlet-name>元素指定的那個名稱。

RequestDispatcher rd = getServletContext().getNamedDispatcher (“catalog”);

 

 

RequestDispatcher接口有兩個方法,允許你在調用的servlet完成初步處理後把請求響應分配到另外一個資源,

forward()方法:

public void forward(ServletRequest request, ServletReponse reponse) throws SwerletException,IOException

forward方法上讓你把請求轉發到另外的Servlet或者jsp或者html等資源,由這個資源接下來負責響應。如:

RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”);

rd. forward(request,response);

 

include()方法:

public void include (ServletRequest request, ServletReponse reponse) throws SwerletException,IOException

include方法讓你的Servlet響應中包含另外一個資源生成內容

RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”);

rd. include(request,response);

 

 

結合WebWork的具體分析

WebWork是由OpenSymphony組織開發實現MVC模式的J2EE Web框架。在介紹完servlet規範的相關內容後,我們看看WebWork是如何注入到一個Servlet中的,假設我們有一個上下文環境爲“/WebWorkdDemo”的WEB應用。

部署描述符

在部署描述符中,我們需要進行如下配置:

<servlet>

<servlet-name>webwork</servlet-name>

<servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>

</servlet>

……

<servlet-mapping>

<servlet-name>webwork</servlet-name>

<url-pattern>*.action</url-pattern>

</servlet-mapping>

我們聲明瞭一個名爲webworkServlet*.action到這個Servlet的映射,這個Servlet就是webwork中的controller,擔任MVC框架中非常重要的控制器角色。

映射請求到Servlet

在XWork的配置文件xwork.xml中有如下片段:

<action name="demo" class=" webworkapp.DemoAction">

    <result name="success" type="dispatcher">

       <param name="location">/demo.jsp</param>

    </result>

</action>

這樣我們由http://localhost:8080/WebWorkDemo/demo.action這個URL向服務器發出請求時,WEB容器首先確定轉到哪一個WEB應用程序,容器將請求URL和上下文環境進行匹配後知道將轉到/WebWorkdDemo這個WEB應用。

接下來容器會在/WebWorkdDemo這個應用的部署描述符中進行查找處理這個請求的servlet,根據後綴*.action找到名稱爲webwork這個Servlet,這樣根據部署描述符,這個請求被映射到webwork中的controller組件com.opensymphony.webwork.dispatcher.ServletDispatcher來處理。這個擔任控制器組件的Servlet在他的service()方法中在根據請求的路徑解析出對應的action來進行處理。

通過上面的的處理,實現了將web請求轉到了webwork中的控制器ServletDispatcher。不止是webwork,實現MVCweb框架都需要進行類似的處理來將web請求轉入到自己的controller.以便進行進一步的處理。

Servlet生存週期

ServletDispatcher這個Servlet的存週期可以如下:

1)      在服務器啓動的時候,容器首先實例化ServletDispatcher

2)        實例化完成後,將調用init()方法,在init方法中執行了以下操作:

Ø         初始化Velocity引擎

Ø         檢查是否支持配置文件重新載入功能。如果支持,每個request請求都將重新裝載xwork.xml配置文件,在開發時非常方便。

Ø         設置一些文件上傳的信息,比如:上傳臨時目錄,上傳的最大字節等。

3)      每次請求都調用service()方法,service方法中執行了以下方法

Ø         通過request請求取得action的命名空間

Ø         根據servlet請求的Path,解析出要調用該請求的Action的名字(actionName)

Ø         創建Action上下文(extraContext),遍歷HttpServletRequest、HttpSession、ServletContext 中的數據,並將其複製到Webwork的Map實現中,至此之後,所有數據操作均在此Map結構中進行,從而將內部結構與Servlet API相分離。

Ø         以上述信息作爲參數,調用ActionProxyFactory創建對應的ActionProxy實例。ActionProxyFactory 將根據Xwork 配置文件(xwork.xml)中的設定,創建ActionProxy實例,ActionProxy中包含了Action的配置信息(包括Action名稱,對應實現類等等)。

Ø         執行proxy的execute()方法

4)      容器移除Servlet 時執行destroy(),在ServletDispatcher這個Servlet中並沒有重寫destroy方法,在移除Servlet時,將什麼也不做。

請求分發

WebWork提供了多種活靈活視圖展現方式,例如還是我們上面在xwork.xml中的配置:

<action name="demo" class=" webworkapp.DemoAction">

    <result name="success" type="dispatcher">

       <param name="location">/demo.jsp</param>

    </result>

</action>

根據以上配置當DemoAction的返回值爲"success"時的處理類型爲"dispatcher",當result的type爲"dispatcher"時,通過javax.servlet.RequestDispatcher的forward()或include()方法將處理結果和表現層融合後展現給用戶

我們可以看看WebWork提供的dispatcher類型Result Type的實現類com.opensymphony .webwork.dispatcher.ServletDispatcherResult中的代碼片斷:

  HttpServletRequest request = ServletActionContext.getRequest();

  HttpServletResponse response = ServletActionContext.getResponse();

  RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);

 

  if (dispatcher == null) {

    response.sendError(404, "result '" + finalLocation + "' not found");  

    return;

  }

 

  if (!response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {

    request.setAttribute("webwork.view_uri", finalLocation);

    request.setAttribute("webwork.request_uri", request.getRequestURI());

   

    dispatcher.forward(request, response);

  } else {

    dispatcher.include(request, response);

  }

ServletDispatcherResult類的從ServletActionContex中得到HttpServletRequest和HttpServletResponse,然後調用request.getRequestDispatcher(finalLocation)方法得到一個RequestDispatcher實例,如果返回的是null,則輸出404頁面未找到的錯誤,否則將調用dispatcher.forward(request, response)或者dispatcher.include(request, response)進行請求分發,將處理結果和表現層融合後展現給用戶。


本文轉自 http://www.blogjava.net/outuo2/archive/2006/06/12/52240.html

發佈了74 篇原創文章 · 獲贊 9 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章