原創講解JSP過濾器和監聽器
BeanSoft(劉長炯) 2007年11月
關于這個問題, 比較復雜的說. 不過我希望通過例子來解釋會方便理解一些.
假設有一個非常危險的任務, 是九死一生. 需要你揣著炸藥包從北平開車走高速路過保定去石家莊把鬼子的碉堡給炸了. 注意: 這個任務十分艱巨, 有可能半路炸藥爆炸. 所以出發前你需要苦練10元一本的<<鐵布衫>>, 還需要立遺囑交代一下后事. 下面是路線圖:
去時路線: 北平 ==> 高速路收費站入口(都有警察和警犬) A? ==> 保定收費站 B ==> 石家莊收費站 C ==> 炸碉堡 D ==> 轉車(剛才的車已經炸沒了) E =>
回來路線: 石家莊收費站 ==> 保定收費站 ==> 高速路收費站出口(都有警察和警犬) A ==> 安全回到北平 G
那么我們先簡介可能發生的情況.? A 點有可能被警察和警犬發現, 所以你只能被扭送回北平.? 即使不被發現, 還可能出現收費時發現10個現大洋一個路口的買路錢沒帶!! 哎, 只能又回去了. 還有最慘的: 高速路入口寫著: 對不起, 到石家莊的路線因為施工不通! 只好回家等著吧.
到了B和C你可能還會被人發現帶了炸藥包, 或者發現買路費少帶了! 極有可能又被扭送回北平, 注意已經在半道了, 是不會讓你繼續到石家莊的, 所以你會被從車上逮下來, 然后轉到警車上帶回來!? 也就是從 B 或者 C 直接返回.
好了, 最佳情況就是你炸了碉堡, 也成功的返回了. 然后你可以開心的把<<鐵布衫>>扔了, 然后宣告遺囑作廢.
OK, 以上過程, 就是過濾器和監聽器的真實案例.
那么炸碉堡這個任務, 就相當于要調用 JSP 或者 Servlet 來獲得執行結果(炸碉堡 D ).? 在執行之前你需要做一些準備工作, 相當于要寫一個
監聽器 com.allanlxf.ums.web.ServiceListener
在里面你可以做一些事情例如初始化資源, 例如上文的苦練<<鐵布衫>>, 立遺囑, 這個相當于代碼中的
public void contextInitialized(ServletContextEvent sce) 這樣一個初始化事件.
那么在整個任務完成之后, 這些事情你就要考慮應該作廢了, 所以需要
public void contextDestroyed(ServletContextEvent sce) 這樣一個銷毀事件, 例如把<<鐵布衫>>扔了, 然后宣告遺囑作廢, 也就是收回資源.
那么過濾器在哪里呢? 它位于任務的 A B C 點. 注意是雙向路程都會經過的. 也就是請求和響應都會經過. 但是過濾器也會檢查不同的情況, 例如 A 點實際上有兩個過濾功能: 查炸藥包和收買路錢. 這就相當于配置了:
??
?????? sessionFilter
???? * *.do
???? * REQUEST
???? * FORWARD
?? ?
兩個過濾功能都要檢查. 如果成功了怎么辦呢? 會繼續讓你走下一個路口, 注意不是讓你直接成功. 也就是代碼:
??????? if(session.getAttribute("user") != null || path.equals("/login"))//若用戶已經登錄并且當前路徑仍然停留在登錄頁面????? {???????????
?????????? chain.doFilter(request, response);//繼續走下面的過濾器或者任務(不保證最終任務, 因為下個過濾器也可能讓你回來)
?????????? 只有當走到 C 點的時候, 下一步才是執行了最終的任務: 執行JSP或者Servlet.
?????? }
反過來怎么辦? 把你扭送上警車, 強行返回! 這樣你連路口 B C 和最終任務都無法訪問了. 也就是不會讓你訪問下一個過濾器和執行最終的 JSP 或者 Serlvet, 可以選擇直接返回, 或者放警車上帶回去.
?????? {
?????????? return;// 直接返回
?????????? 或者 response.sendRedirect(request.getContextPath() + "/login.jsp");//則扭送上警車, 強行返回到警察局
?????? }
那么在 E 點發生了什么? 也就是你可能替換掉了原來的 response 對象,? 也就是換車. 這意味著你可以在過濾器里私下修改請求和響應對象.
雖然已經不那么抽象了, 但是要理解可能還是需要耐心體會的.
=====================================================================
注: 原始問題
??? 1.請看下面一段代碼和其注釋:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class SessionFilter extends HttpFilter
{
?? public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
?????????????? throws ServletException, IOException
?? {
?????? String path = request.getServletPath();//取得該servlet的路徑名稱
?????? path = path.substring(0, path.indexOf("."));?? //獲得路徑中.之前的部分
?????? HttpSession session = request.getSession();//從請求中取得session為得是從session中讀取用戶是否登錄的標志值
?????? if(session.getAttribute("user") != null || path.equals("/login"))//若用戶已經登錄并且當前路徑仍然停留在登錄頁面
?????? {???????????
???????? *? chain.doFilter(request, response);//則將當前濾鏡加入到濾鏡鏈條當中
?????? }else//若用戶尚未成功登錄
?????? {
?????????? response.sendRedirect(request.getContextPath() + "/login.jsp");//則使用請求重定向轉到登錄頁面
?????? }???????
?? }
}
/*
* 該Filter濾鏡類所過濾的是用戶在瀏覽器中輸入的
* 當前web application的訪問路徑,通過判斷用戶是
* 否成功登錄而決定是否對訪問路徑加以限止
* 在該web application的web.xml配置文件中為
* 該filte濾鏡做了相應配置
*/
請問: 上面代碼FilterChain對象盛裝的是Filter對象, 可它是怎么工作的啊?
加*號的語句是何用意啊,不是過濾路徑嗎, 為什么要將請求與響應加入呢?
2.請看下面一段代碼:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public abstract class HttpFilter implements Filter
{
?? private FilterConfig config;
?? public void init(FilterConfig config) throws ServletException
?? {
?????? this.config = config;
?????? init();
?? }
?? public void init() throws ServletException
?? {
?? }
?? public String getInitParameter(String name)
?? {
?????? return config.getInitParameter(name);
?? }
?? public ServletContext getServletContext()
?? {
?????? return config.getServletContext();
?? }
?? public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
?????????????? throws ServletException, IOException
?? {
?????? doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
?? }
?? public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
?????????????? throws ServletException, IOException;
?? public void destroy()
?? {
?? }
}
請問: 該類就這樣寫了一下,沒有包含任何行為,它如何完成過濾工作啊?
3.請看下面代碼:
package com.allanlxf.ums.web;
import javax.servlet.*;
//import javax.servlet.http.*;
import com.allanlxf.ums.service.SystemService;
import com.allanlxf.ums.service.StudentService;
public class ServiceListener implements ServletContextListener
{
?? public void contextInitialized(ServletContextEvent sce)
?? {
?????? ServletContext application = sce.getServletContext();
?????? StudentService service = new StudentService();
?????? application.setAttribute("studentService", service);
?????? SystemService systemService = new SystemService();
?????? application.setAttribute("systemService", systemService);
?? }
?? public void contextDestroyed(ServletContextEvent sce)
?? {
?? }
}
/*
*本類是該web application的監聽器類,在該類中,
*將兩個無為本系統提供服務的兩個對象寫入到了
*application隱含對象中(這樣保證在該系統的任何
*地方都能夠取得這兩個服務類對象的引用)
*在本系統的web.xml文件中對該監聽器做了配置
*/
請問: 為什么要將服務類對象寫入到程序上下文中呢,直接創建不也一樣嗎?
???? 監聽器只有這樣的功能嗎,是否還有其它功能呢?
3.請看下面的配置文件內容:
??
?????? sessionFilter
?????? com.allanlxf.ums.web.SessionFilter
?? ???
??
?????? sessionFilter
???? * *.do
???? * REQUEST
???? * FORWARD
?? ???????
??
?????? com.allanlxf.ums.web.ServiceListener
???
請問: 加*號的三項是什么意思?
當初老師講的時候,許多基本的原則原理尚不清楚,這些根本沒有聽明白,當然,老師也未細講,只說會用即可, 可是若不能明白其本質,用也只能用這一點兒, 怎么成呢, 希望您能夠比較詳悉的講一下. thank you very much!

文章來源:
http://m.tkk7.com/beansoft/archive/2007/11/09/159374.html