Java中Filter、Servlet、Listener的學習

1、Filter的功能

filter功能,它使用戶可以改變一個 request和修改一個response. Filter 不是一個servlet,它不能產生一個response,它能夠在一個request到達servlet之前預處理request,也可以在離開 servlet時處理response.換種說法,filter其實是一個”servlet chaining”(servlet 鏈).

一個Filter包括:

1)、在servlet被調用之前截獲;

2)、在servlet被調用之前檢查servlet request;

3)、根據需要修改request頭和request數據;

4)、根據需要修改response頭和response數據;

5)、在servlet被調用之后截獲.

服務器每次只調用setFilterConfig方法一次準備filter 的處理;調用doFilter方法多次以處理不同的請求.FilterConfig接口有方法可以找到filter名字及初始化參數信息.服務器可以設置 FilterConfig為空來指明filter已經終結。

每一個filter從doFilter()方法中得到當前的request及response.在這個方法里,可以進行任何的針對request及 response的操作.(包括收集數據,包裝數據等).filter調用chain.doFilter()方法把控制權交給下一個filter.一個 filter在doFilter()方法中結束.如果一個filter想停止request處理而獲得對response的完全的控制,那它可以不調用下 一個filter

例子:

首先新建一個Filter

[java]view plaincopy

/**

*

*/

packagecom.ee.filter;

importjava.io.IOException;

importjavax.servlet.Filter;

importjavax.servlet.FilterChain;

importjavax.servlet.FilterConfig;

importjavax.servlet.ServletException;

importjavax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;

/**

*?@author?Administrator

*

*/

publicclassLogFilterimplementsFilter?{

privateFilterConfig?filterConfig;

publicFilterConfig?getFilterConfig()?{

System.err.println("...getFilterConfig...");

returnfilterConfig;

}

publicvoidsetFilterConfig(FilterConfig?filterConfig)?{

System.err.println("...setFilterConfig...");

this.filterConfig?=?filterConfig;

}

/*?(non-Javadoc)

*?@see?javax.servlet.Filter#destroy()

*/

@Override

publicvoiddestroy()?{

System.err.println("...filter?destroy...");

}

/*?(non-Javadoc)

*?@see?javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,?javax.servlet.ServletResponse,?javax.servlet.FilterChain)

*/

@Override

publicvoiddoFilter(ServletRequest?request,?ServletResponse?response,

FilterChain?chain)throwsIOException,?ServletException?{

System.err.println("...doFilter...");

chain.doFilter(request,?response);//看到這沒,這只要是傳遞下一個Filter

}

/*?(non-Javadoc)

*?@see?javax.servlet.Filter#init(javax.servlet.FilterConfig)

*/

@Override

publicvoidinit(FilterConfig?filterConfig)throwsServletException?{

System.err.println("...init?Filter...");

this.filterConfig?=?filterConfig;

}

}

在web.xml里配置

[html]view plaincopy

LogFilter

com.ee.filter.LogFilter

LogFilter

/*

啟動運行

可以看到...init Filter...首先在TOMCAT啟動時即被打印,然后在運行里面再看到...doFilter...被打印。

2、servlet功能

1.Servlet 是什么?

Servlet是使用Java Servlet 應用程序設計接口(API)及相關類和方法的 Java 程序。除了 Java Servlet API,Servlet 還可以使用用以擴展和添加到 API 的 Java 類軟件包。Servlet 在啟用 Java 的 Web 服務器上或應用服務器上運行并擴展了該服務器的能力。Java servlet對于Web服務器就好象Java applet對于Web瀏覽器。Servlet裝入Web服務器并在Web服務器內執行,而applet裝入Web瀏覽器并在Web瀏覽器內執行。Java Servlet API 定義了一個servlet 和Java使能的服務器之間的一個標準接口,這使得Servlets具有跨服務器平臺的特性。

Servlet 通過創建一個框架來擴展服務器的能力,以提供在 Web 上進行請求和響應服務。當客戶機發送請求至服務器時,服務器可以將請求信息發送給 Servlet,并讓 Servlet 建立起服務器返回給客戶機的響應。 當啟動 Web 服務器或客戶機第一次請求服務時,可以自動裝入 Servlet。裝入后, Servlet 繼續運行直到其它客戶機發出請求。Servlet 的功能涉及范圍很廣。例如,Servlet 可完成如下功能:

(1) 創建并返回一個包含基于客戶請求性質的動態內容的完整的 HTML頁面。

(2) 創建可嵌入到現有 HTML 頁面中的一部分 HTML 頁面(HTML 片段)。

(3) 與其它服務器資源(包括數據庫和基于 Java 的應用程序)進行通信。

(4) 用多個客戶機處理連接,接收多個客戶機的輸入,并將結果廣播到多個客戶機上。例如,Servlet 可以是多參與者的游戲服務器。

(5) 當允許在單連接方式下傳送數據的情況下,在瀏覽器上打開服務器至applet的新連接,并將該連

接保持在打開狀態。當允許客戶機和服務器簡單、高效地執行會話的情況下,applet也可以啟動客戶瀏覽器和服務器之間的連接。可以通過定制協議或標準(如 IIOP)進行通信。

(6) 對特殊的處理采用 MIME 類型過濾數據,例如圖像轉換和服務器端包括(SSI)。

(7) 將定制的處理提供給所有服務器的標準例行程序。例如,Servlet 可以修改如何認證用戶。

2.Servlet 的生命周期

Servlet 的生命周期始于將它裝入 Web 服務器的內存時,并在終止或重新裝入 Servlet 時結束。

(1) 初始化

在下列時刻裝入 Servlet:

如果已配置自動裝入選項,則在啟動服務器時自動裝入

在服務器啟動后,客戶機首次向 Servlet 發出請求時

重新裝入 Servlet 時裝入 Servlet 后,服務器創建一個 Servlet 實例并且調用 Servlet 的 init() 方法。在初始化階段,Servlet 初始化參數被傳遞給 Servlet 配置對象。

(2) 請求處理

對于到達服務器的客戶機請求,服務器創建特定于請求的一個“請求”對象和一個“響應”對象。服務器調用 Servlet 的 service() 方法,該方法用于傳遞“請求”和“響應”對象。service() 方法從“請求”對象獲得請求信息、處理該請求并用“響應”對象的方法以將響應傳回客戶機。service() 方法可以調用其它方法來處理請求,例如 doGet()、doPost() 或其它的方法。

(3) 終止

當服務器不再需要 Servlet, 或重新裝入 Servlet 的新實例時,服務器會調用 Servlet 的 destroy() 方法。

3. Java Servlet API

Java Servlet 開發工具(JSDK)提供了多個軟件包,在編寫 Servlet 時需要用到這些軟件包。其中包括兩個用于所有 Servlet 的基本軟件包:javax.servlet 和 javax.servlet.http。可從sun公司的Web站點下載 Java Servlet 開發工具。 下面主要介紹javax.servlet.http提供的HTTP Servlet應用編程接口。

HTTP Servlet 使用一個 HTML 表格來發送和接收數據。要創建一個 HTTP Servlet,請擴展 HttpServlet 類, 該類是用專門的方法來處理 HTML 表格的 GenericServlet 的一個子類。 HTML 表單是由 和 標記定義的。表單中典型地包含輸入字段(如文本輸入字段、復選框、單選按鈕和選擇列表)和用于提交數據的按鈕。當提交信息時,它們還指定服務器應執行哪一個Servlet(或其它的程序)。 HttpServlet 類包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是繼承的。

(1) init() 方法

在 Servlet 的生命期中,僅執行一次 init() 方法。它是在服務器裝入 Servlet 時執行的。 可以配置服務器,以在啟動服務器或客戶機首次訪問 Servlet 時裝入 Servlet。 無論有多少客戶機訪問 Servlet,都不會重復執行 init() 。

缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法來覆蓋它,典型的是管理服務器端資源。 例如,可能編寫一個定制 init() 來只用于一次裝入 GIF 圖像,改進 Servlet 返回 GIF 圖像和含有多個客戶機請求的性能。另一個示例是初始化數據庫連接。缺省的 init() 方法設置了 Servlet 的初始化參數,并用它的 ServletConfig 對象參數來啟動配置, 因此所有覆蓋 init() 方法的 Servlet 應調用 super.init() 以確保仍然執行這些任務。在調用 service() 方法之前,應確保已完成了 init() 方法。

(2) service() 方法

service() 方法是 Servlet 的核心。每當一個客戶請求一個HttpServlet 對象,該對象的service() 方法就要被調用,而且傳遞給這個方法一個“請求”(ServletRequest)對象和一個“響應”(ServletResponse)對象作為參數。 在 HttpServlet 中已存在 service() 方法。缺省的服務功能是調用與 HTTP 請求的方法相應的 do 功能。例如, 如果 HTTP 請求方法為 GET,則缺省情況下就調用 doGet() 。Servlet 應該為 Servlet 支持的 HTTP 方法覆蓋 do 功能。因為 HttpServlet.service() 方法會檢查請求方法是否調用了適當的處理方法,不必要覆蓋 service() 方法。只需覆蓋相應的 do 方法就可以了。

當一個客戶通過HTML 表單發出一個HTTP POST請求時,doPost()方法被調用。與POST請求相關的參數作為一個單獨的HTTP 請求從瀏覽器發送到服務器。當需要修改服務器端的數據時,應該使用doPost()方法。

當一個客戶通過HTML 表單發出一個HTTP GET請求或直接請求一個URL時,doGet()方法被調用。與GET請求相關的參數添加到URL的后面,并與這個請求一起發送。當不會修改服務器端的數據時,應該使用doGet()方法。

Servlet的響應可以是下列幾種類型:

一個輸出流,瀏覽器根據它的內容類型(如text/HTML)進行解釋。

一個HTTP錯誤響應, 重定向到另一個URL、servlet、JSP。

(3) destroy() 方法

destroy() 方法僅執行一次,即在服務器停止且卸裝Servlet 時執行該方法。典型的,將 Servlet 作為服務器進程的一部分來關閉。缺省的 destroy() 方法通常是符合要求的,但也可以覆蓋它,典型的是管理服務器端資源。例如,如果 Servlet 在運行時會累計統計數據,則可以編寫一個 destroy() 方法,該方法用于在未裝入 Servlet 時將統計數字保存在文件中。另一個示例是關閉數據庫連接。

當服務器卸裝 Servlet 時,將在所有 service() 方法調用完成后,或在指定的時間間隔過后調用 destroy() 方法。一個Servlet 在運行service() 方法時可能會產生其它的線程,因此請確認在調用 destroy() 方法時,這些線程已終止或完成。

(4) GetServletConfig()方法

GetServletConfig()方法返回一個 ServletConfig 對象,該對象用來返回初始化參數和  ServletContext。ServletContext 接口提供有關servlet 的環境信息。

(5) GetServletInfo()方法

GetServletInfo()方法是一個可選的方法,它提供有關servlet 的信息,如作者、版本、版權。

當服務器調用sevlet 的Service()、doGet()和doPost()這三個方法時,均需要 “請求”和“響應”對象作為參數。“請求”對象提供有關請求的信息,而“響應”對象提供了一個將響應信息返回給瀏覽器的一個通信途徑。javax.servlet 軟件包中的相關類為ServletResponse和ServletRequest,而javax.servlet.http 軟件包中的相關類為HttpServletRequest 和 HttpServletResponse。Servlet 通過這些對象與服務器通信并最終與客戶機通信。Servlet 能通過調用“請求”對象的方法獲知客戶機環境,服務器環境的信息和所有由客戶機提供的信息。Servlet 可以調用“響應”對象的方法發送響應,該響應是準備發回客戶機的。

例子:

創建一個servlet

[java]view plaincopy

/**

*

*/

packagecom.ee.servlet;

importjava.io.IOException;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServlet;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

/**

*?@author?Administrator

*

*/

publicclassLogServletextendsHttpServlet?{

/**

*

*/

privatestaticfinallongserialVersionUID?=?1L;

@Override

protectedvoiddoGet(HttpServletRequest?req,?HttpServletResponse?resp)

throwsServletException,?IOException?{

doPost(req,?resp);

}

@Override

protectedvoiddoPost(HttpServletRequest?req,?HttpServletResponse?resp)

throwsServletException,?IOException?{

System.err.println("...doPost(req,?resp)...");

}

}

在web.xml中的配置:

[html]view plaincopy

LogServlet

com.ee.servlet.LogServlet

LogServlet

/*

它的攔截規則:

當一個請求發送到servlet容器的時候,容器先會將請求的url減去當前應用上下文的路徑作為servlet的映射url,比如我訪問的是http://localhost/test/aaa.html,我的應用上下文是test,容器會將http://localhost/test去掉,剩下的/aaa.html部分拿來做servlet的映射匹配。這個映射匹配過程是有順序的,而且當有一個servlet匹配成功以后,就不會去理會剩下的servlet了(filter不同,后文會提到)。其匹配規則和順序如下:

1.???? 精確路徑匹配。例子:比如servletA 的url-pattern為 /test,servletB的url-pattern為 /* ,這個時候,如果我訪問的url為http://localhost/test,這個時候容器就會先 進行精確路徑匹配,發現/test正好被servletA精確匹配,那么就去調用servletA,也不會去理會其他的servlet了。

2.???? 最長路徑匹配。例子:servletA的url-pattern為/test/*,而servletB的url-pattern為/test/a/*,此時訪問http://localhost/test/a時,容器會選擇路徑最長的servlet來匹配,也就是這里的servletB。

3.???? 擴展匹配,如果url最后一段包含擴展,容器將會根據擴展選擇合適的servlet。例子:servletA的url-pattern:*.action

4.???? 如果前面三條規則都沒有找到一個servlet,容器會根據url選擇對應的請求資源。如果應用定義了一個default servlet,則容器會將請求丟給default servlet

3、Listener功能

它是基于觀察者模式設計的,Listener 的設計對開發 Servlet 應用程序提供了一種快捷的手段,能夠方便的從另一個縱向維度控制程序和數據。目前 Servlet 中提供了 5 種兩類事件的觀察者接口,它們分別是:4 個 EventListeners 類型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和 2 個 LifecycleListeners 類型的,ServletContextListener、HttpSessionListener。如下圖所示:


Listener是Servlet的監聽器,它可以監聽客戶端的請求、服務端的操作等。通過監聽器,可以自動激發一些操作,比如監聽在線的用戶的數量。當增加一個HttpSession時,就激發sessionCreated(HttpSessionEvent se)方法,這樣就可以給在線人數加1。常用的監聽接口有以下幾個:

ServletContextAttributeListener監聽對ServletContext屬性的操作,比如增加、刪除、修改屬性。

ServletContextListener監聽ServletContext。當創建ServletContext時,激發contextInitialized(ServletContextEvent sce)方法;當銷毀ServletContext時,激發contextDestroyed(ServletContextEvent sce)方法。

HttpSessionListener監聽HttpSession的操作。當創建一個Session時,激發session Created(HttpSessionEvent se)方法;當銷毀一個Session時,激發sessionDestroyed (HttpSessionEvent se)方法。

HttpSessionAttributeListener監聽HttpSession中的屬性的操作。當在Session增加一個屬性時,激發attributeAdded(HttpSessionBindingEvent se) 方法;當在Session刪除一個屬性時,激發attributeRemoved(HttpSessionBindingEvent se)方法;當在Session屬性被重新設置時,激發attributeReplaced(HttpSessionBindingEvent se) 方法。

下面我們開發一個具體的例子,這個監聽器能夠統計在線的人數。在ServletContext初始化和銷毀時,在服務器控制臺打印對應的信息。當ServletContext里的屬性增加、改變、刪除時,在服務器控制臺打印對應的信息。

要獲得以上的功能,監聽器必須實現以下3個接口:

HttpSessionListener

ServletContextListener

ServletContextAttributeListener

例子:

[java]view plaincopy

/**

*

*/

packagecom.ee.listener;

importjavax.servlet.ServletContextAttributeEvent;

importjavax.servlet.ServletContextAttributeListener;

importjavax.servlet.ServletContextEvent;

importjavax.servlet.ServletContextListener;

importjavax.servlet.http.HttpSessionEvent;

importjavax.servlet.http.HttpSessionListener;

/**

*?@author?Administrator

*

*/

publicclassOnlineUserListenerimplementsHttpSessionListener,

ServletContextListener,?ServletContextAttributeListener?{

privatelongonlineUserCount?=0;

publiclonggetOnlineUserCount()?{

returnonlineUserCount;

}

/*?(non-Javadoc)

*?@see?javax.servlet.ServletContextAttributeListener#attributeAdded(javax.servlet.ServletContextAttributeEvent)

*/

@Override

publicvoidattributeAdded(ServletContextAttributeEvent?arg0)?{

}

/*?(non-Javadoc)

*?@see?javax.servlet.ServletContextAttributeListener#attributeRemoved(javax.servlet.ServletContextAttributeEvent)

*/

@Override

publicvoidattributeRemoved(ServletContextAttributeEvent?arg0)?{

}

/*?(non-Javadoc)

*?@see?javax.servlet.ServletContextAttributeListener#attributeReplaced(javax.servlet.ServletContextAttributeEvent)

*/

@Override

publicvoidattributeReplaced(ServletContextAttributeEvent?attributeEvent)?{

System.err.println("...attributeReplaced...");

}

/*?(non-Javadoc)

*?@see?javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)

*/

@Override

publicvoidcontextDestroyed(ServletContextEvent?arg0)?{

}

/*?(non-Javadoc)

*?@see?javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)

*/

@Override

publicvoidcontextInitialized(ServletContextEvent?arg0)?{

}

/*?(non-Javadoc)

*?@see?javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)

*/

@Override

publicvoidsessionCreated(HttpSessionEvent?httpSessionEvent)?{

onlineUserCount?++;

toUpdateCount(httpSessionEvent);

}

/*?(non-Javadoc)

*?@see?javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)

*/

@Override

publicvoidsessionDestroyed(HttpSessionEvent?httpSessionEvent)?{

onlineUserCount?--;

toUpdateCount(httpSessionEvent);

}

privatevoidtoUpdateCount(HttpSessionEvent?httpSessionEvent){

httpSessionEvent.getSession().setAttribute("onlineUserCount",?onlineUserCount);

}

}

Web.xml

[html]view plaincopy

com.ee.listener.OnlineUserListener

JSP頁面:

[html]view plaincopy

<%@?pagelanguage="java"contentType="text/html;?charset=UTF-8"

pageEncoding="UTF-8"%>

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

推薦閱讀更多精彩內容