Servlet 3.0之Servlet Context

1. ServletContext接口介紹

ServletContext接口定義了servlet運行時Web應用的servlet視圖。容器提供者負責提供容器中ServletContext接口的實現。使用ServletContext對象,servlet能夠為事件記錄日志,獲取定位資源的URL引用,以及設置并存儲其它servlet能夠訪問的屬性。

ServletContext位于Web服務器大家都熟悉的路徑下。例如,一個servlet上下文可以位于http://www.mycorp.com/catalog。所有以/catalog開頭的路徑,即所謂的上下文路徑,會映射到ServletContext相關的Web應用中。

2. 一個ServletContext 接口的范圍

部署在容器中的每一個Web應用都關聯著一個ServletContext接口的實例對象。對于分布在很多虛擬機器上的容器,一個Web應用中每一個JVM對應一個ServletContext實例。

容器中不作為Web應用一部分部署的servlets,它們隱式地作為一個默認Web應用的一部分,并且有一個默認的ServletContext。在分布式容器中,默認的ServletContext并不是分布式的,且必須僅存在于一個JVM中。

3. 初始化參數

下列ServletContext接口的方法允許servlet訪問應用開發者在部署描述符中設置的相關初始化參數。

  • getInitParameter
  • getInitParameterNames

應用開發者用初始化參數來傳達配置信息。典型的例子是一個網站站長的電子郵箱或者保存關鍵數據的系統名稱。

4. 配置方法

從servlet3.0開始,下列方法被添加到ServletContext中,用戶可以編程方式定義servlet、filter以及它們映射的url模式。這些方法僅能在應用初始化階段ServletContextListener實現的contextInitialized方法或者ServletContainerInitializer實現的onStartup方法中被調用。

除了添加Servlets或者Filters,也能查找與Servlet、Filter或者Servlet、Filters上的所有Registration對象的map對應的Registration對象的一個實例。

如果傳遞給ServletContextListener的contextInitialized方法的ServletContext既不是web.xml或web-fragment.xml里面申明的,也不是通過注解@WebListener申明的,那么為servlets,filters和listeners編程配置定義的所有方法必須拋出一個UnsupportedOperationException異常。

  1. 以編程方法添加和配置Servlets
    編程式地為一個上下文添加一個Servlet的能力對于框架開發者來說十分有用。比如,一個框架可以用這個方法申明一個controller servlet。這個方法的返回值是一個ServletRegistration或者一個允許你進一步設置servlet的其它參數,如init-params,url-mapping等等的ServletRegistration.Dynamic對象。這個方法有三個重寫版本。

  2. addServlet(String servletName, String className)
    這個方法允許應用編程式地申明一個servlet。它通過指定的名字和類名把servlet添加到servlet上下文中。

  3. addServlet(String servletName, Servlet servlet)
    這個方法允許應用編程式地申明一個servlet。它通過指定的名字和servlet實例添加一個servlet到servlet上下文中。

  4. addServlet(String servletName, Class <? extends Servlet> servletClass)
    這個方法允許應用編程式地申明一個servlet。它通過指定的名字和servlet類的實例添加一個servlet到servlet上下文中。

  5. <T extends Servlet> T createServlet(Class<T> clazz)
    這個方法實例化一個指定的Servlet類。這個方法必須支持除了@WebServlet之外,可應用于Servlets上的所有注解。在通過調用上述的addServlet(String, Servlet)注冊到ServletContext之前,方法返回的Servlet實例可以進一步設置。

  6. ServletRegistration getServletRegistration(String, servletName)
    這個方法返回與指定servlet對應的ServletRegistration,或者如果不存在此名字對應的ServletRegistration就返回null。

    如果沒有servlets注冊到ServletContext,將返回一個空的Map。返回的Map包括與所有申明的和注解的servlet對應的ServletRegistration對象,以及與所有通過addServlet方法添加到ServletContext的servlet對應的ServletRegistration對象。返回的Map的任何變化不得影響ServletContext。

    如果ServletContext 被傳遞到既不在web.xml或者web-fragment.xml中聲明,也沒有通過javax.servlet.annotation.WebListener注解的ServletContextListenercontextInitialized方法,那么一個UNsupportedOperationException異常將會拋出。

  7. 以編程方法添加和配置Filters

  8. addFilter(String filterName, String className)
    這個方法允許應用編程式地申明一個filter。它把指定名字和類名的filter添加到web應用中。

  9. addFilter(String filterName, Filter filter)
    這個方法允許通過編程式地申明一個filter。他把指定名字和類名的filter添加到web應用中。

  10. addFilter(String filterName, Class<? extends Filter> filterClass)
    這個方法允許應用通過編程方式申明一個filter。它將會把指定名字和servlet類的實例的filter添加到web應用中。

  11. <T extends Filter> T createFilter(Class<T> clazz)
    這個方法實例化指定的Filter類。這個方法必須支持可應用于Filter上的所有注解。通過調用addServlet(String, Filter)方法注冊到ServletContext之前,返回的Filter實例可以進一步配置。指定的Filter類必須定義一個無參構造函數來實例化自己。

  12. FilterRegistration getFilterRegistration(String filterName)
    這個方法返回與之定義filter名字對應的FilterRegistration,或者不存在指定名字的FilterRegistration將返回null。如果ServletContext傳遞給一個ServletContextListener的contextInitialized方法,但是ServletContextListener既沒有在web.xml或者web-fragment.xml中申明,或者沒有使用javax.servlet.annotation.WebListener中的注解,那么將會拋出UnsupportedOperationException異常。

  13. Map<String, <? extends FilterRegistration>> getServletRegistrations()
    這個方法返回一個ServletRegistration對象的map,它的key對應所有ServletContext中注冊的servlet的名字。如果沒有通過ServletContext注冊的servlet,那么將返回一個空的map。返回的Map包含了與所有申明的和注解的servlet對應的ServletRegistration對象,同時也包括通過任意addServlet方法添加的所有servlets對應的ServletRegistration對象。返回的Map的任何變化不影響ServletContext。如果ServletContext傳遞給一個ServletContextListener的contextInitialized方法,但是ServletContextListener既沒有在web.xml或者web-fragment.xml中申明,也沒有通過javax.servlet.annotation.WebListener注解,那么將會拋出UnsupportedOperationException異常。

  14. 編程式地添加和配置listeners

  15. void addListener(String className)
    添加指定類名的listener到ServletContext。指定類名的類將通過ServletContext中與應用相關的類加載器加載,而且必須實現下列一個或多個的接口:

 * javax.servlet.ServletContextAttributeListener
 * javax.servlet.ServletRequestListener
 * javax.servlet.ServletRequestAttributeListener
 * javax.servlet.http.HttpSessionLister
 * javax.servlet.http.HttpSessionAttributeListener

 如果ServletContext被傳遞給ServletContainerInitializer的onStartup方法,除了上述接口,指定名字的類也可以實現*javax.servlet.ServletContextListener*。作為這個方法調用的一部分,容器必須加載指定類名的類來確保它實現了必要接口中的一個。如果指定listener是一個調用順序與聲明順序一致的listener接口的實例,也就是說,如果它實現了*javax.servlet.ServletRequestListener* 或者*javax.servlet.http.HttpSessionListener*,那么新的listener將會被添加到那個接口的listeners的有序列表的末尾。

1. **<T extends EventListener> void addListener(T t)**

添加指定listener到ServletContext。指定的listener必須是下列一個或者多個接口的實例:
* javax.servlet.ServletContextAttributeListener
* javax.servlet.ServletRequestListener
* javax.servlet.ServletRequestAttributeListener
* javax.servlet.http.HttpSessionListener
* javax.servlet.http.HttpSessionAttributeListener

  如果ServletContext被傳遞給ServletContainerInitializer的onStartup方法,除了上述接口,指定listener也可以是*javax.servlet.ServletContextListener*的實例。
 如果指定listener是一個調用順序與聲明順序一致的listener接口的實例,也就是,如果它實現了*javax.servlet.ServletRequestListener*或者*javax.servlet.http.HttpSessionListener*,那么新的listener將會添加到那個接口的listener的有序列表的末尾。
1. **void addListener(Class<? extends EventListener> listenerClass)**

添加指定類類型的listener到ServletContext。指定listener類必須實現一個或者多個下列接口:
* javax.servlet.ServletContextAttributeListener
* javax.servlet.ServletRequestListener
* javax.servlet.ServletRequestAttributeListener
* javax.servlet.http.HttpSessionListener
* javax.servlet.http.HttpSessionAttributeListener

 如果ServletContext被傳遞給ServletContainerInitializer的onStartup方法,除了上述接口,指定listener類也可以實現*javax.servlet.ServletContextListener*。

如果指定listener類實現了一個調用順序與聲明順序一致的listener,也就說,如果它實現了javax.servlet.ServletRequestListener或者javax.servlet.http.HttpSessionListener,那么新的listener將會被添加到那個接口的listener的有序列表的末尾。

  1. <T extends EventListener> void createListener(Class<T> clazz)
    這個方法實例化指定的EventListener類。具體的EventListener類必須實現下列至少一個接口:

    • javax.servlet.ServletContextAttributeListener
    • javax.servlet.ServletRequestListener
    • javax.servlet.ServletRequestAttributeListener
    • javax.servlet.http.HttpSessionListener
    • javax.servlet.http.HttpSessionAttributeListener

    這個方法必須支持適用于本規范中定義的上述listener接口的所有注解。在它調用addListener(T t)注冊到ServletContext之前,返回的EventListener實例可以進一步被設置。指定的EventListener類必須定義一個用來實例化自己的無參構造函數。

  2. 對于以編程方式添加的Servlets、Filters和Listeners的注解處理要求
    當使用編程的API添加一個servlet或者創建一個servlet,除去需要一個實例參數的addServlet,下列注解必須在討論的和定義元數據的類中被檢查,除非它們通過調用ServletRegistration.Dynamic/ServletRegistration的API被重寫:

 * @ServletSecurity
 * @RunAs
 * @DeclareRoles
 * @MultipartConfig

 對于Filters和Listener,沒有注解需要被檢查。

所有編程式添加或者編程式創建,而并不是通過接受一個實例參數的方法來添加的組件(Servlets, Filters, Listeners)上的資源注入,僅當組件是一個Managed Bean時才會被支持。
想知道更多關于什么是Managed Bean的詳細信息請參考Java EE 6部分和JSR 299中定義Managed Bean規范。

5. Context屬性

一個servlet能把一個對象屬性通過名字綁定到上下文中。任何綁定到上下文的屬性,相同Web應用中的其它任何servlet都可訪問。下列ServletContext接口的方法允許訪問該功能:

  • setAttribute
  • getAttribute
  • getAttributeNames
  • removeAttribute
  1. 分布式容器中的上下文屬性
    上下文屬性位于它們被創建的JVM。這防止在分布式環境中ServletContext屬性被共享。分布式環境中,當信息需要在servlet中共享,這些信息應該放在session、存放于數據庫或者在企業JavaBean組件中設置。

6. 資源

ServletContext接口通過它下列方法,僅對是Web應用一部分的靜態文件提供直接訪問,包括HTML、GIF以及JPEG文件。

  • getResource
  • getResourceAsStream

getResource和getResourceAsStream方法接收一個'/'開頭的字符串參數,這個參數指定了資源相對于上下文根路徑,或者相對于web應用WEB-INF/lib目錄里JAR文件的META-INF/resource目錄的資源路徑。在查找WEB-INF/lib目錄里任何JAR文件之前,這些方法將首先為請求的資源搜索web應用的根路徑。WEB-INF/lib目錄里JAR文件被掃描的順序沒有定義。文檔的層次結構可能存在服務器的文件系統,Web應用的歸檔文件,遠端服務器或者其它地方。

這些方法不用來獲取動態內容。
比如,在支持JSP規范的容器中,表單getResource("index.jsp")的方法調用將返回JSP源碼,而不是處理過的輸出。
第9章中,Dispatching Requests將會討論更多訪問動態內容。

Web應用中完整的資源列表能通過調用getResourcePaths(String path) 方法被訪問到。關于這個方法的語義詳情可以在這個規范的API文檔中找到。

7. 多個主機和Servlet上下文

Web服務器可以在一個服務器上支持多個共享一個IP地址的邏輯主機。這種能力有時被稱之為虛擬主機。在這種場景下,每個邏輯主機一定有它自己的servlet上下文或者servlet上下文的集合。Servlets上下文不能在虛擬主機之間被共享。

8. 重新加載的注意事項

對于便捷開發,雖然一個容器提供者實現一個類重新加載方案并不是必須的,但是任何實現必須確保所有的servlets以及它們使用的類在單個類加載器范圍內被加載。這個需求需要用來確保應用按照開發者期望的方式運行。
作為開發輔助,通知綁定了監聽者的session的完整語義應該被容器支持,以便類重新加載時監控會話終止。

以前的一些容器會創建新的類加載器來加載一個servlet,以便區分于用來加載其它servlet的類加載器或者servlet上下文中使用的類。這會引起servlet上下文中的對象引用指向不是預期類或者對象,并且會引起不確定的行為。這個要求用來防止因需求產生的新的類加載器的問題。

  1. 臨時工作目錄
    臨時存儲目錄對于每一個servlet上下文都是必要的。
    Servlet容器必須為每一個servlet上下文提供一個臨時目錄,并且讓它可以通過javax.servlet.context.tempdir上下文屬性被訪問到。此屬性相關的對象必須是java.io.File 類型。

這個要求在許多servlet引擎實現中提供了共同的便利。當servlet容器啟動時,容器并不要求維護臨時目錄的內容,但是被要求來確保在servlet容器中,一個servlet容器的的臨時目錄內容對其它Web應用的servlet上下文不可見。

翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容