一個Web應用是一個Web服務器上眾多資源的集合,它包括了servlets,HTML頁面,類,和其它組成一個完整應用的資源。Web應用可以被捆綁和運行在來自多個供應商的多種容器上。
一、在Web Servers里面的Web應用
在一個Web服務器中,一個Web應用把一個指定的路徑作為根目錄。比如,一個分類應用能夠位于"http://www.mycorp.com/catalog"。
所有以此為前綴的請求都會被路由到代表分類應用的ServletContext。
一個servlet能夠為Web應用的自動構建建立規則。比如,~user/映射能夠被用來映射到一個位于/home/user/public_html/ 的Web應用。
默認地,在任意時刻一個Web應用的一個時刻必須運行在一個VM上面。如果這個應用通過它的描述符被標記為分布式的,那么這個行為能夠被重寫。被標記為分布式的應用必須比不同Web應用遵循更多限制規則。
二、與ServletContext的關系
一個servlet容器必須在一個Web應用和一個ServletContext之間確保一對一的對應關系。一個ServletContext對象提供一個有應用視圖的servlet。
三、一個Web應用的元素
一個Web應用必須包含下列項目:
- Servlets
- JSP頁面
- Utility Classes
- 靜態文件(HTML,Image,sounds,etc.)
- 客戶端Java applets,beans和類
- 黏合上述元素的描述符元信息
四、部署層級
這份規范定義了一個用來部署和打包的層次結構,這種層次結構能夠存在一個開放文件系統中,一個歸檔文件中或者其它形式中。
五、目錄結構
一個Web應用以一個結構化的層次目錄存在。這個層次的根作為屬于應用一部分的文件的文檔根目錄。
比如,對于一個Web容器中用/catalog上下文路徑的一個Web應用,在Web應用層次的基目錄,或者在一個META-INF/resources目錄下包含index.html,WEB-INF/lib中的JAR文件中的index.html可以用來滿足來自/catalog/index.html的請求。如果一個index.html文件同時出現在根上下文中和應用的WEB-INF/lib目錄里的JAR文件中的META-INF/resources目錄里,那么根上下文中的文件必須被使用。匹配URLs到上下文路徑的規則在章節Mapping Requests Servlets中。由于應用的上下文路徑決定了Web應用的內容的的URL命名空間,web容器必須拒絕定義了會在URL命名空間中引起潛在沖突的上下文路徑的應用。比如,通過用相同上下文路徑部署第二個Web應用就會出現這種情況。由于請求會按照大小寫敏感方式被映射到資源,潛在沖突的決定也必須對大小寫敏感。
一個特殊目錄存在于名字為WEB-INF的應用層級中。這個目錄包含了所有不在應用文檔根目錄的應用相關的東西。大多數WEB-INF 節點并不是應用的公開文檔樹的一部分。除了靜態資源和打包在WEB-INF/lib目錄中的JAR文件中META-INF/resources中的JSPs,包含在WEB-INF目錄中的其它文件不會被容器直接暴露給客戶端。然而,WEB-INF目錄的內容對ServletContext上使用getResource和getResourceAsStream方法調用的servlet代碼可見,并且可以使用RequestDispatcher方法調用來暴露。因此,如果應用開發者需要從servlet代碼中訪問應用那些不直接暴露給客戶端的詳細配置信息,他可以把它們放到這個目錄下面。由于請求會以大小寫敏感的方式被映射到資源,比如,客戶端對/WEB-INF/foo,/WEb-iNf/foo的請求不應該獲得位于/WEB-INF目錄下以及任何形式的目錄列表的Web應用的內容。
WEB-INF目錄的內容如下:
- /WEB-INF/web.xml部署描述符
- 放servlet和utility classes的/WEB-INF/classes/目錄。這個目錄下的類必須能讓應用的類加載器訪問到。
- 放JAVA歸檔文件的/WEB-INF/lib/.jar*的區域。這些文件包含servlets,beans,打包在JAR中的JSPs和靜態資源,以及對Web應用有用的utility類。Web應用的類加載器必須能夠從任何歸檔文件中加載類。
Web應用類加載器必須先從WEB-INF/classes加載類,然后從WEB-INF/lib目錄中的JARs中加載類。除了打包在JAR文件中的靜態資源,來自客戶端去訪問WEB-INF/目錄里資源的任何請求必須返回一個帶404狀態碼的response。
-
應用目錄結構的例子
下面是示例Web應用中所有文件的列表:/index.html /howto.jsp /feedback.jsp /images/banner.gif /images/jumping.gif /WEB-INF/web.xml /WEB-INF/lib/jspbean.jar /WEB-INF/lib/catalog.jar!/META-INF/resouces/catalog/moreOffers/books.html /WEB-INF/classes/com/mycorp/servlets/MyServlet.class /WEB-INF/classes/com/mycorp/util/MyUtils.class
六、Web應用歸檔文件
Web應用能使用標準的Java歸檔工具被打包進Web ARchive format(WAR)文件中。比如,對于問題跟蹤的一個應用可能被分布在叫做issuetrack.war的歸檔文件中。
當打包進這種形式中,將會出現一個META-INF目錄,它包含了對Java 歸檔工具很有用的信息。這個目錄一定不能被容器直接作為內容來響應一個Web客戶端的請求,盡管它的內容可以通過ServletContext上的getResource和getResourceAsStream調用對servlet代碼可見。任何訪問META-INF目錄下的資源的請求必須返回帶404狀態碼的response。
七、Web應用部署描述符
Web應用部署描述符包含下列配置類型和部署信息:
- ServletContext初始化參數
- Session Configuration
- Servlet/JSP定義
- Servlet/JSP映射
- MIME類型映射
- Welcome File list
- Error Pages
- Security
-
擴展上的依賴
當很多應用使用相同的代碼或者資源時,它們通常會在容器中被安裝為庫文件。這些文件很通用或者是無需考慮兼容性就能使用的標準APIs。被一個或者多個應用使用的文件將會作為Web應用的一部分被訪問。容器必須為這些庫文件提供一個目錄。這個目錄中的文件必須要在多個Web應用之間被訪問到。這個目錄的位置根據具體的容器而定。servlet容器用來加載這些庫文件的類加載器對同一個JVM中所有的Web應用都應該一樣。這個類加載器實例必須在Web應用類加載器的父加載器鏈的某個位置上。
應用開發者需要知道哪些擴展要被安裝在一個Web容器中,并且容器需要知道WAR中哪些依賴servlets有這些庫,以便保留兼容性。
依賴這個擴展的應用開發者必須在WAR文件中提供一個META-INF/MANIFEST.MF入口,這個入口列出了WAR需要的所有擴展。manifest入口的格式應該遵循標準JAR manifest格式。在Web應用的部署期間,Web容器必須遵循下列規則,給應用配上正確的擴展版本。
Web容器必須能夠識別這樣的聲明依賴,它們被寫在WAR里面WEB-INF/lib 目錄入口下的任意類庫JARs的manifest入口里。
如果一個Web容器不能夠滿足這種方式里面聲明的依賴,它應該用一個錯誤信息來拒絕這種應用。
-
Web應用類加載器
容器使用來從WAR里加載一個servlet的類加載器必須允許開發者使用getResource并遵循普通Java SE機制來加載WAR里面的庫JARs里的任意資源。如Java EE license協議描述的,不是Java EE產品一部分的servlet容器不應該允許應用來重寫Java SE平臺不允許被修改的類,比如在java.和javax.命名空間里面的類。容器不應該允許應用來重寫或者訪問容器的實現類。
推薦實現應用類加載器以便WAR中的類和資源優先于容器端庫JARs里面的類和資源。一個實現必須確保對于每一個部署在一個容器中的web應用,對Thread.currentThread.getContextClassLoader()的調用必須返回一個實現了本章具體協議的的ClassLoader實例。更進一步說,ClassLoader實例必須對于每個部署的web應用必須是一個單獨的實例。容器需要在任何回調函數之前設置線程上下文ClassLoader到一個web應用中。
八、替換一個Web應用
一個服務器用一個新版本來替換一個應用,而無需重啟容器。當一個應用被替換,容器應該提供一個魯棒的方法來保留應用中的會話數據。
九、錯誤處理
-
請求屬性
一個Web應用必須能夠詳細指出錯誤何時出現,應用中的其它資源被用來提供錯誤響應的內容體。這些資源的詳情配置在部署描述符中。
如果錯誤處理的問題是錯誤處理器是一個servlet或者一個JSP頁面:
- 被容器創建的未包裝過的原始request和response對象被傳遞給這個servlet或者JSP頁面。
- 如果RequestDispatcher.forward已經被執行,請求路徑和屬性被設置給錯誤資源。
- Table中的請求屬性必須被設置:
Table 請求屬性和它們的類型
Request Attribute | Type |
---|---|
javax.servlet.error.status_code | java.lang.Integet |
javax.servlet.error.exception_type | java.lang.Class |
javax.servlet.error.message | java.lang.String |
javax.servlet.error.exception | java.lang.Throwable |
javax.servlet.error.request_uri | java.lang.String |
javax.servlet.error.servlet_name | java.lang.String |
這些屬性允許servlet根據狀態碼,異常類型,錯誤信息,異常對象傳遞,出現錯誤的servlet的請求的URI,以及出現錯誤的servlet的邏輯名字產生具體的內容。
把異常對象引到2.3版本的規范的屬性列表中,異常類型和錯誤信息屬性就是多余的了。它們被保留,是為了對以前版本的API做向后兼容。
-
錯誤頁面
當一個servlet出現一個錯誤,為了允許開發者定制返回給Web客戶端的內容外觀,部署描述符定義一個錯誤頁面描述的列表。當一個servlet或者filter在response上為一個具體錯誤碼調用sendError時,或者這個servlet產生一個傳遞給容器的異?;蛘咤e誤時,這個語法允許資源的配置被容器返回。
如果sendError方法在response上被調用,容器會查找為Web應用使用狀態碼語法和嘗試匹配聲明的錯誤頁面列表。如果有一個匹配的,容器返回匹配位置入口表明的資源。
一個servlet或者filter可能在一個請求期間拋出下列異常:
- 運行時異常或者錯誤
- ServletException或者其子類
- IOException或者其子類
Web應用可以使用exception-type元素聲明錯誤頁面。這種情況下,容器通過對比拋出的異常和使用exception-type 定義的錯誤頁面列表來匹配異常。一個匹配會讓容器返回在位置入口指明的資源。類層次結構中最近的匹配會生效。
如果沒有包含一個exception-type的error-page聲明符合使用類層次匹配,并且拋出的異常是一個ServletException或者其子類,容器獲取被包裝過的異常,如被ServletException.getRootCause方法定義的一樣。在錯誤頁面聲明上做第二次通過,再次嘗試匹配錯誤頁面聲明,但是使用包裝過的異常。
在部署描述符中使用exception-type元素的錯誤頁面異常對exception-type的類名必須唯一。類似的,使用status-code*元素的錯誤頁面在部署描述符中對于狀態碼必須唯一。
當使用RequestDispatcher或者filter.doFilter方法調用時,上述錯誤頁面機制不會干涉錯誤何時出現。這種方式下,一個使用RequestDispatcher的filter或者servlet有機會處理出現的錯誤。
如果一個servlet產生了一個沒有被上述錯誤頁面機制處理的錯誤時,容器必須確保發送一個狀態碼為500的response。
默認的servlet和容器將會使用sendError方法發送4xx和5xx狀態的response,所以錯誤機制可以被調用。默認servlet和容器將會為2xx何3xx的responses使用setStatus,并且不會調用錯誤頁面機制。
如果應用正在使用上述Asynchronous processing的異步操作,應用負責處理應用創建的線程中的所有錯誤。容器可以通過AsyncContext.start處理線程中的錯誤。為了處理AsyncContext.dispatch期間的出現的錯誤,參考“在dispatch方法執行期間可能出現的任何錯誤或異常必須被容器捕獲和處理”。
-
錯誤Filters
錯誤頁面機制在容器創建的未包裝/未過濾的request和response對象上起作用。Filters和RequestDispatcher可以用來在一個錯誤response生成之前指明被應用的filters。
十、歡迎文件
Web應用開發者能在Web應用部署描述符中定義一個叫做歡迎文件的部分URIs的有序列表。對這個列表的部署描述符語法會在下述Web應用部署描述符機制中說明。
當有一個請求WAR文件的目錄入口對應的URI沒有被映射到Web組件,這個機制的目的就是允許開發者為容器指定一個用來追加到URIs的部分URIs的有序列表。
請參考下列通用示例:
一個歡迎文件index.html能夠被定義,以便對host:port/webapp/directory/ 的請求會返回host:port/webapp/directory.index.html 給客戶端。上述的directory是WAR中的一個入口,而沒有被映射到一個servlet或者JSP頁面。
如果一個Web容器接收一個合法的部分請求,Web容器必須檢查定義在部署描述符中的歡迎文件列表。歡迎文件列表是一個沒有尾部或前導'/'的部分URIs的有序列表。Web服務器必須把每一個歡迎文件按照部署描述符中的順序追加到部分請求,并且檢查WAR中的靜態資源是否被映射到那個請求URI。如果沒有找到映射,Web服務器必須再次把每個歡迎文件按照部署描述符中的順序追加到部分請求,并且檢查這個servlet是否被映射到那個請求URI。Web容器必須把請求發送到WAR中第一個匹配到的資源。容器可能把請求發送到歡迎資源,方式或者是forward,或者是redirect,或者是區別于直接請求的一個容器詳細機制。
如果按照上述方式沒有找到匹配的歡迎文件,容器可以按照它能找到的合適方式去處理請求。對于一些配置,這意味著返回一個目錄列表或者對另外一些配置就返回404的response。
考慮這樣一個Web應用:
- 部署描述符列出了下列歡迎文件
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list> - WAR中的靜態內容
/foo/index.html
/foo/default.jsp
/foo/orderform.html
/foo/home.gif
/catalog/default.jsp
/catalog/products/shop.jsp
/catalog/products/register.jsp - /foo 將會被重定向到/foo/
- /foo/ 將會被重定向到/foo/index.html
- /catalog 將會被重定向到/catalog/
- /catalog/ 將會被重定向到/catalog/default.jsp
- /catalog/index.html 將會引發一個404 not found
- /catalog/products 將會被重定向到/catalog/products/
- /catalog/products/會被傳遞給默認servlet,如果有默認servlet。如果沒有匹配的默認servlet,請求可能引發一個404 not found,也可能引發一個包含shop.jsp 和register.jsp** 的目錄列表,或者可能引發被容器定義的其它行為。
- 上述所有的靜態內容能夠和上述打包在jar文件META-INF/resources目錄里列出的內容一起被打包進JAR文件中。
十一、Web應用環境
不屬于Java EE兼容實現的Servlet容器被提倡(但不是必須)實現"Web Application Environment and Java EE specification"中描述的應用環境功能。如果它們沒有實現要求支持這個環境的功能,在部署依賴它們的應用時,容器應該發出一個警告。
十二、Web應用部署
當一個web應用被部署到一個容器中,在web應用開始處理客戶端請求之前,下列步驟必須按順序執行。
- 給每個在部署描述符中<listener>元素定義的事件監聽器實例化一個實例。
- 為實現了ServletContextListener的監聽者實例,調用contextInitialized()方法。
- 給部署描述符中<filter>元素定義的每一個filter實例化一個實例,并調用每個filter實例的init()方法。
十三、一個web.xml部署描述符的總結
如果一個web應用不包含任何Servlet,Filter,或者監聽組件,或者用注解聲明的相同組件,那么這個應用不要求包含一個web.xml。換句話說,一個僅包含靜態文件或者JSP頁面的應用不要求一個web.xml。
翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010