一個(gè)Web應(yīng)用是一個(gè)Web服務(wù)器上眾多資源的集合怜珍,它包括了servlets畏陕,HTML頁面拓劝,類视卢,和其它組成一個(gè)完整應(yīng)用的資源湿弦。Web應(yīng)用可以被捆綁和運(yùn)行在來自多個(gè)供應(yīng)商的多種容器上。
一腾夯、在Web Servers里面的Web應(yīng)用
在一個(gè)Web服務(wù)器中颊埃,一個(gè)Web應(yīng)用把一個(gè)指定的路徑作為根目錄蔬充。比如,一個(gè)分類應(yīng)用能夠位于"http://www.mycorp.com/catalog"班利。
所有以此為前綴的請(qǐng)求都會(huì)被路由到代表分類應(yīng)用的ServletContext饥漫。
一個(gè)servlet能夠?yàn)閃eb應(yīng)用的自動(dòng)構(gòu)建建立規(guī)則。比如罗标,~user/映射能夠被用來映射到一個(gè)位于/home/user/public_html/ 的Web應(yīng)用庸队。
默認(rèn)地,在任意時(shí)刻一個(gè)Web應(yīng)用的一個(gè)時(shí)刻必須運(yùn)行在一個(gè)VM上面闯割。如果這個(gè)應(yīng)用通過它的描述符被標(biāo)記為分布式的彻消,那么這個(gè)行為能夠被重寫。被標(biāo)記為分布式的應(yīng)用必須比不同Web應(yīng)用遵循更多限制規(guī)則宙拉。
二宾尚、與ServletContext的關(guān)系
一個(gè)servlet容器必須在一個(gè)Web應(yīng)用和一個(gè)ServletContext之間確保一對(duì)一的對(duì)應(yīng)關(guān)系。一個(gè)ServletContext對(duì)象提供一個(gè)有應(yīng)用視圖的servlet谢澈。
三煌贴、一個(gè)Web應(yīng)用的元素
一個(gè)Web應(yīng)用必須包含下列項(xiàng)目:
- Servlets
- JSP頁面
- Utility Classes
- 靜態(tài)文件(HTML,Image锥忿,sounds牛郑,etc.)
- 客戶端Java applets,beans和類
- 黏合上述元素的描述符元信息
四敬鬓、部署層級(jí)
這份規(guī)范定義了一個(gè)用來部署和打包的層次結(jié)構(gòu)淹朋,這種層次結(jié)構(gòu)能夠存在一個(gè)開放文件系統(tǒng)中,一個(gè)歸檔文件中或者其它形式中钉答。
五瑞你、目錄結(jié)構(gòu)
一個(gè)Web應(yīng)用以一個(gè)結(jié)構(gòu)化的層次目錄存在。這個(gè)層次的根作為屬于應(yīng)用一部分的文件的文檔根目錄希痴。
比如,對(duì)于一個(gè)Web容器中用/catalog上下文路徑的一個(gè)Web應(yīng)用春感,在Web應(yīng)用層次的基目錄砌创,或者在一個(gè)META-INF/resources目錄下包含index.html,WEB-INF/lib中的JAR文件中的index.html可以用來滿足來自/catalog/index.html的請(qǐng)求鲫懒。如果一個(gè)index.html文件同時(shí)出現(xiàn)在根上下文中和應(yīng)用的WEB-INF/lib目錄里的JAR文件中的META-INF/resources目錄里嫩实,那么根上下文中的文件必須被使用。匹配URLs到上下文路徑的規(guī)則在章節(jié)Mapping Requests Servlets中窥岩。由于應(yīng)用的上下文路徑?jīng)Q定了Web應(yīng)用的內(nèi)容的的URL命名空間甲献,web容器必須拒絕定義了會(huì)在URL命名空間中引起潛在沖突的上下文路徑的應(yīng)用。比如颂翼,通過用相同上下文路徑部署第二個(gè)Web應(yīng)用就會(huì)出現(xiàn)這種情況晃洒。由于請(qǐng)求會(huì)按照大小寫敏感方式被映射到資源慨灭,潛在沖突的決定也必須對(duì)大小寫敏感。
一個(gè)特殊目錄存在于名字為WEB-INF的應(yīng)用層級(jí)中球及。這個(gè)目錄包含了所有不在應(yīng)用文檔根目錄的應(yīng)用相關(guān)的東西氧骤。大多數(shù)WEB-INF 節(jié)點(diǎn)并不是應(yīng)用的公開文檔樹的一部分。除了靜態(tài)資源和打包在WEB-INF/lib目錄中的JAR文件中META-INF/resources中的JSPs吃引,包含在WEB-INF目錄中的其它文件不會(huì)被容器直接暴露給客戶端筹陵。然而滓侍,WEB-INF目錄的內(nèi)容對(duì)ServletContext上使用getResource和getResourceAsStream方法調(diào)用的servlet代碼可見惊奇,并且可以使用RequestDispatcher方法調(diào)用來暴露。因此喉刘,如果應(yīng)用開發(fā)者需要從servlet代碼中訪問應(yīng)用那些不直接暴露給客戶端的詳細(xì)配置信息庐氮,他可以把它們放到這個(gè)目錄下面语稠。由于請(qǐng)求會(huì)以大小寫敏感的方式被映射到資源,比如旭愧,客戶端對(duì)/WEB-INF/foo颅筋,/WEb-iNf/foo的請(qǐng)求不應(yīng)該獲得位于/WEB-INF目錄下以及任何形式的目錄列表的Web應(yīng)用的內(nèi)容。
WEB-INF目錄的內(nèi)容如下:
- /WEB-INF/web.xml部署描述符
- 放servlet和utility classes的/WEB-INF/classes/目錄输枯。這個(gè)目錄下的類必須能讓應(yīng)用的類加載器訪問到议泵。
- 放JAVA歸檔文件的/WEB-INF/lib/.jar*的區(qū)域。這些文件包含servlets桃熄,beans先口,打包在JAR中的JSPs和靜態(tài)資源,以及對(duì)Web應(yīng)用有用的utility類瞳收。Web應(yīng)用的類加載器必須能夠從任何歸檔文件中加載類碉京。
Web應(yīng)用類加載器必須先從WEB-INF/classes加載類,然后從WEB-INF/lib目錄中的JARs中加載類螟深。除了打包在JAR文件中的靜態(tài)資源谐宙,來自客戶端去訪問WEB-INF/目錄里資源的任何請(qǐng)求必須返回一個(gè)帶404狀態(tài)碼的response。
-
應(yīng)用目錄結(jié)構(gòu)的例子
下面是示例Web應(yīng)用中所有文件的列表:/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應(yīng)用歸檔文件
Web應(yīng)用能使用標(biāo)準(zhǔn)的Java歸檔工具被打包進(jìn)Web ARchive format(WAR)文件中凡蜻。比如,對(duì)于問題跟蹤的一個(gè)應(yīng)用可能被分布在叫做issuetrack.war的歸檔文件中垢箕。
當(dāng)打包進(jìn)這種形式中划栓,將會(huì)出現(xiàn)一個(gè)META-INF目錄,它包含了對(duì)Java 歸檔工具很有用的信息条获。這個(gè)目錄一定不能被容器直接作為內(nèi)容來響應(yīng)一個(gè)Web客戶端的請(qǐng)求忠荞,盡管它的內(nèi)容可以通過ServletContext上的getResource和getResourceAsStream調(diào)用對(duì)servlet代碼可見。任何訪問META-INF目錄下的資源的請(qǐng)求必須返回帶404狀態(tài)碼的response。
七委煤、Web應(yīng)用部署描述符
Web應(yīng)用部署描述符包含下列配置類型和部署信息:
- ServletContext初始化參數(shù)
- Session Configuration
- Servlet/JSP定義
- Servlet/JSP映射
- MIME類型映射
- Welcome File list
- Error Pages
- Security
-
擴(kuò)展上的依賴
當(dāng)很多應(yīng)用使用相同的代碼或者資源時(shí)堂油,它們通常會(huì)在容器中被安裝為庫文件。這些文件很通用或者是無需考慮兼容性就能使用的標(biāo)準(zhǔn)APIs素标。被一個(gè)或者多個(gè)應(yīng)用使用的文件將會(huì)作為Web應(yīng)用的一部分被訪問称诗。容器必須為這些庫文件提供一個(gè)目錄。這個(gè)目錄中的文件必須要在多個(gè)Web應(yīng)用之間被訪問到头遭。這個(gè)目錄的位置根據(jù)具體的容器而定寓免。servlet容器用來加載這些庫文件的類加載器對(duì)同一個(gè)JVM中所有的Web應(yīng)用都應(yīng)該一樣。這個(gè)類加載器實(shí)例必須在Web應(yīng)用類加載器的父加載器鏈的某個(gè)位置上计维。
應(yīng)用開發(fā)者需要知道哪些擴(kuò)展要被安裝在一個(gè)Web容器中袜香,并且容器需要知道WAR中哪些依賴servlets有這些庫,以便保留兼容性鲫惶。
依賴這個(gè)擴(kuò)展的應(yīng)用開發(fā)者必須在WAR文件中提供一個(gè)META-INF/MANIFEST.MF入口蜈首,這個(gè)入口列出了WAR需要的所有擴(kuò)展。manifest入口的格式應(yīng)該遵循標(biāo)準(zhǔn)JAR manifest格式欠母。在Web應(yīng)用的部署期間欢策,Web容器必須遵循下列規(guī)則,給應(yīng)用配上正確的擴(kuò)展版本赏淌。
Web容器必須能夠識(shí)別這樣的聲明依賴踩寇,它們被寫在WAR里面WEB-INF/lib 目錄入口下的任意類庫JARs的manifest入口里。
如果一個(gè)Web容器不能夠滿足這種方式里面聲明的依賴六水,它應(yīng)該用一個(gè)錯(cuò)誤信息來拒絕這種應(yīng)用俺孙。
-
Web應(yīng)用類加載器
容器使用來從WAR里加載一個(gè)servlet的類加載器必須允許開發(fā)者使用getResource并遵循普通Java SE機(jī)制來加載WAR里面的庫JARs里的任意資源。如Java EE license協(xié)議描述的掷贾,不是Java EE產(chǎn)品一部分的servlet容器不應(yīng)該允許應(yīng)用來重寫Java SE平臺(tái)不允許被修改的類睛榄,比如在java.和javax.命名空間里面的類。容器不應(yīng)該允許應(yīng)用來重寫或者訪問容器的實(shí)現(xiàn)類想帅。
推薦實(shí)現(xiàn)應(yīng)用類加載器以便WAR中的類和資源優(yōu)先于容器端庫JARs里面的類和資源场靴。一個(gè)實(shí)現(xiàn)必須確保對(duì)于每一個(gè)部署在一個(gè)容器中的web應(yīng)用,對(duì)Thread.currentThread.getContextClassLoader()的調(diào)用必須返回一個(gè)實(shí)現(xiàn)了本章具體協(xié)議的的ClassLoader實(shí)例港准。更進(jìn)一步說旨剥,ClassLoader實(shí)例必須對(duì)于每個(gè)部署的web應(yīng)用必須是一個(gè)單獨(dú)的實(shí)例。容器需要在任何回調(diào)函數(shù)之前設(shè)置線程上下文ClassLoader到一個(gè)web應(yīng)用中叉趣。
八、替換一個(gè)Web應(yīng)用
一個(gè)服務(wù)器用一個(gè)新版本來替換一個(gè)應(yīng)用该押,而無需重啟容器疗杉。當(dāng)一個(gè)應(yīng)用被替換,容器應(yīng)該提供一個(gè)魯棒的方法來保留應(yīng)用中的會(huì)話數(shù)據(jù)。
九烟具、錯(cuò)誤處理
-
請(qǐng)求屬性
一個(gè)Web應(yīng)用必須能夠詳細(xì)指出錯(cuò)誤何時(shí)出現(xiàn)梢什,應(yīng)用中的其它資源被用來提供錯(cuò)誤響應(yīng)的內(nèi)容體。這些資源的詳情配置在部署描述符中朝聋。
如果錯(cuò)誤處理的問題是錯(cuò)誤處理器是一個(gè)servlet或者一個(gè)JSP頁面:
- 被容器創(chuàng)建的未包裝過的原始request和response對(duì)象被傳遞給這個(gè)servlet或者JSP頁面嗡午。
- 如果RequestDispatcher.forward已經(jīng)被執(zhí)行,請(qǐng)求路徑和屬性被設(shè)置給錯(cuò)誤資源冀痕。
- Table中的請(qǐng)求屬性必須被設(shè)置:
Table 請(qǐng)求屬性和它們的類型
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根據(jù)狀態(tài)碼荔睹,異常類型,錯(cuò)誤信息言蛇,異常對(duì)象傳遞僻他,出現(xiàn)錯(cuò)誤的servlet的請(qǐng)求的URI,以及出現(xiàn)錯(cuò)誤的servlet的邏輯名字產(chǎn)生具體的內(nèi)容腊尚。
把異常對(duì)象引到2.3版本的規(guī)范的屬性列表中吨拗,異常類型和錯(cuò)誤信息屬性就是多余的了。它們被保留婿斥,是為了對(duì)以前版本的API做向后兼容劝篷。
-
錯(cuò)誤頁面
當(dāng)一個(gè)servlet出現(xiàn)一個(gè)錯(cuò)誤,為了允許開發(fā)者定制返回給Web客戶端的內(nèi)容外觀民宿,部署描述符定義一個(gè)錯(cuò)誤頁面描述的列表娇妓。當(dāng)一個(gè)servlet或者filter在response上為一個(gè)具體錯(cuò)誤碼調(diào)用sendError時(shí),或者這個(gè)servlet產(chǎn)生一個(gè)傳遞給容器的異晨备撸或者錯(cuò)誤時(shí)峡蟋,這個(gè)語法允許資源的配置被容器返回。
如果sendError方法在response上被調(diào)用华望,容器會(huì)查找為Web應(yīng)用使用狀態(tài)碼語法和嘗試匹配聲明的錯(cuò)誤頁面列表蕊蝗。如果有一個(gè)匹配的,容器返回匹配位置入口表明的資源赖舟。
一個(gè)servlet或者filter可能在一個(gè)請(qǐng)求期間拋出下列異常:
- 運(yùn)行時(shí)異撑钇荩或者錯(cuò)誤
- ServletException或者其子類
- IOException或者其子類
Web應(yīng)用可以使用exception-type元素聲明錯(cuò)誤頁面。這種情況下宾抓,容器通過對(duì)比拋出的異常和使用exception-type 定義的錯(cuò)誤頁面列表來匹配異常子漩。一個(gè)匹配會(huì)讓容器返回在位置入口指明的資源。類層次結(jié)構(gòu)中最近的匹配會(huì)生效石洗。
如果沒有包含一個(gè)exception-type的error-page聲明符合使用類層次匹配幢泼,并且拋出的異常是一個(gè)ServletException或者其子類,容器獲取被包裝過的異常讲衫,如被ServletException.getRootCause方法定義的一樣缕棵。在錯(cuò)誤頁面聲明上做第二次通過,再次嘗試匹配錯(cuò)誤頁面聲明,但是使用包裝過的異常招驴。
在部署描述符中使用exception-type元素的錯(cuò)誤頁面異常對(duì)exception-type的類名必須唯一篙程。類似的,使用status-code*元素的錯(cuò)誤頁面在部署描述符中對(duì)于狀態(tài)碼必須唯一别厘。
當(dāng)使用RequestDispatcher或者filter.doFilter方法調(diào)用時(shí)虱饿,上述錯(cuò)誤頁面機(jī)制不會(huì)干涉錯(cuò)誤何時(shí)出現(xiàn)。這種方式下触趴,一個(gè)使用RequestDispatcher的filter或者servlet有機(jī)會(huì)處理出現(xiàn)的錯(cuò)誤氮发。
如果一個(gè)servlet產(chǎn)生了一個(gè)沒有被上述錯(cuò)誤頁面機(jī)制處理的錯(cuò)誤時(shí),容器必須確保發(fā)送一個(gè)狀態(tài)碼為500的response雕蔽。
默認(rèn)的servlet和容器將會(huì)使用sendError方法發(fā)送4xx和5xx狀態(tài)的response折柠,所以錯(cuò)誤機(jī)制可以被調(diào)用。默認(rèn)servlet和容器將會(huì)為2xx何3xx的responses使用setStatus批狐,并且不會(huì)調(diào)用錯(cuò)誤頁面機(jī)制扇售。
如果應(yīng)用正在使用上述Asynchronous processing的異步操作,應(yīng)用負(fù)責(zé)處理應(yīng)用創(chuàng)建的線程中的所有錯(cuò)誤嚣艇。容器可以通過AsyncContext.start處理線程中的錯(cuò)誤承冰。為了處理AsyncContext.dispatch期間的出現(xiàn)的錯(cuò)誤,參考“在dispatch方法執(zhí)行期間可能出現(xiàn)的任何錯(cuò)誤或異常必須被容器捕獲和處理”食零。
-
錯(cuò)誤Filters
錯(cuò)誤頁面機(jī)制在容器創(chuàng)建的未包裝/未過濾的request和response對(duì)象上起作用困乒。Filters和RequestDispatcher可以用來在一個(gè)錯(cuò)誤response生成之前指明被應(yīng)用的filters。
十贰谣、歡迎文件
Web應(yīng)用開發(fā)者能在Web應(yīng)用部署描述符中定義一個(gè)叫做歡迎文件的部分URIs的有序列表娜搂。對(duì)這個(gè)列表的部署描述符語法會(huì)在下述Web應(yīng)用部署描述符機(jī)制中說明。
當(dāng)有一個(gè)請(qǐng)求WAR文件的目錄入口對(duì)應(yīng)的URI沒有被映射到Web組件吱抚,這個(gè)機(jī)制的目的就是允許開發(fā)者為容器指定一個(gè)用來追加到URIs的部分URIs的有序列表百宇。
請(qǐng)參考下列通用示例:
一個(gè)歡迎文件index.html能夠被定義,以便對(duì)host:port/webapp/directory/ 的請(qǐng)求會(huì)返回host:port/webapp/directory.index.html 給客戶端秘豹。上述的directory是WAR中的一個(gè)入口携御,而沒有被映射到一個(gè)servlet或者JSP頁面。
如果一個(gè)Web容器接收一個(gè)合法的部分請(qǐng)求既绕,Web容器必須檢查定義在部署描述符中的歡迎文件列表啄刹。歡迎文件列表是一個(gè)沒有尾部或前導(dǎo)'/'的部分URIs的有序列表。Web服務(wù)器必須把每一個(gè)歡迎文件按照部署描述符中的順序追加到部分請(qǐng)求凄贩,并且檢查WAR中的靜態(tài)資源是否被映射到那個(gè)請(qǐng)求URI誓军。如果沒有找到映射,Web服務(wù)器必須再次把每個(gè)歡迎文件按照部署描述符中的順序追加到部分請(qǐng)求疲扎,并且檢查這個(gè)servlet是否被映射到那個(gè)請(qǐng)求URI昵时。Web容器必須把請(qǐng)求發(fā)送到WAR中第一個(gè)匹配到的資源廓译。容器可能把請(qǐng)求發(fā)送到歡迎資源,方式或者是forward债查,或者是redirect,或者是區(qū)別于直接請(qǐng)求的一個(gè)容器詳細(xì)機(jī)制瓜挽。
如果按照上述方式?jīng)]有找到匹配的歡迎文件盹廷,容器可以按照它能找到的合適方式去處理請(qǐng)求。對(duì)于一些配置久橙,這意味著返回一個(gè)目錄列表或者對(duì)另外一些配置就返回404的response俄占。
考慮這樣一個(gè)Web應(yīng)用:
- 部署描述符列出了下列歡迎文件
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list> - WAR中的靜態(tài)內(nèi)容
/foo/index.html
/foo/default.jsp
/foo/orderform.html
/foo/home.gif
/catalog/default.jsp
/catalog/products/shop.jsp
/catalog/products/register.jsp - /foo 將會(huì)被重定向到/foo/
- /foo/ 將會(huì)被重定向到/foo/index.html
- /catalog 將會(huì)被重定向到/catalog/
- /catalog/ 將會(huì)被重定向到/catalog/default.jsp
- /catalog/index.html 將會(huì)引發(fā)一個(gè)404 not found
- /catalog/products 將會(huì)被重定向到/catalog/products/
- /catalog/products/會(huì)被傳遞給默認(rèn)servlet,如果有默認(rèn)servlet淆衷。如果沒有匹配的默認(rèn)servlet缸榄,請(qǐng)求可能引發(fā)一個(gè)404 not found,也可能引發(fā)一個(gè)包含shop.jsp 和register.jsp** 的目錄列表祝拯,或者可能引發(fā)被容器定義的其它行為甚带。
- 上述所有的靜態(tài)內(nèi)容能夠和上述打包在jar文件META-INF/resources目錄里列出的內(nèi)容一起被打包進(jìn)JAR文件中。
十一佳头、Web應(yīng)用環(huán)境
不屬于Java EE兼容實(shí)現(xiàn)的Servlet容器被提倡(但不是必須)實(shí)現(xiàn)"Web Application Environment and Java EE specification"中描述的應(yīng)用環(huán)境功能鹰贵。如果它們沒有實(shí)現(xiàn)要求支持這個(gè)環(huán)境的功能,在部署依賴它們的應(yīng)用時(shí)康嘉,容器應(yīng)該發(fā)出一個(gè)警告碉输。
十二、Web應(yīng)用部署
當(dāng)一個(gè)web應(yīng)用被部署到一個(gè)容器中亭珍,在web應(yīng)用開始處理客戶端請(qǐng)求之前敷钾,下列步驟必須按順序執(zhí)行。
- 給每個(gè)在部署描述符中<listener>元素定義的事件監(jiān)聽器實(shí)例化一個(gè)實(shí)例肄梨。
- 為實(shí)現(xiàn)了ServletContextListener的監(jiān)聽者實(shí)例阻荒,調(diào)用contextInitialized()方法。
- 給部署描述符中<filter>元素定義的每一個(gè)filter實(shí)例化一個(gè)實(shí)例峭范,并調(diào)用每個(gè)filter實(shí)例的init()方法财松。
十三、一個(gè)web.xml部署描述符的總結(jié)
如果一個(gè)web應(yīng)用不包含任何Servlet纱控,F(xiàn)ilter辆毡,或者監(jiān)聽組件,或者用注解聲明的相同組件甜害,那么這個(gè)應(yīng)用不要求包含一個(gè)web.xml舶掖。換句話說,一個(gè)僅包含靜態(tài)文件或者JSP頁面的應(yīng)用不要求一個(gè)web.xml尔店。
翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010