Filter(過(guò)濾器)簡(jiǎn)介
Filter 的基本功能是對(duì) Servlet 容器調(diào)用 Servlet 的過(guò)程進(jìn)行攔截,從而在 Servlet 進(jìn)行響應(yīng)處理的前后實(shí)現(xiàn)一些特殊的功能。
在 Servlet API 中定義了三個(gè)接口類來(lái)開(kāi)供開(kāi)發(fā)人員編寫 Filter 程序:Filter, FilterChain, FilterConfig
Filter 程序是一個(gè)實(shí)現(xiàn)了 Filter 接口的 Java 類,與 Servlet 程序相似,它由 Servlet 容器進(jìn)行調(diào)用和執(zhí)行
Filter 程序需要在 web.xml 文件中進(jìn)行注冊(cè)和設(shè)置它所能攔截的資源:Filter 程序可以攔截 Jsp, Servlet, 靜態(tài)圖片文件和靜態(tài) html 文件
Filter 的基本工作原理
當(dāng)在 web.xml 中注冊(cè)了一個(gè) Filter 來(lái)對(duì)某個(gè) Servlet 程序進(jìn)行攔截處理時(shí),這個(gè) Filter 就成了 Servlet 容器與該 Servlet 程序的通信線路上的一道關(guān)卡,該 Filter 可以對(duì) Servlet 容器發(fā)送給 Servlet 程序的請(qǐng)求和 Servlet 程序回送給 Servlet 容器的相應(yīng)進(jìn)行攔截,可以決定是否將請(qǐng)求繼續(xù)傳遞給 Servlet 程序,以及對(duì)請(qǐng)求和相應(yīng)信息是否進(jìn)行修改
在一個(gè) web 應(yīng)用程序中可以注冊(cè)多個(gè) Filter 程序,每個(gè) Filter 程序都可以對(duì)一個(gè)或一組 Servlet 程序進(jìn)行攔截。
若有多個(gè) Filter 程序?qū)δ硞€(gè) Servlet 程序的訪問(wèn)過(guò)程進(jìn)行攔截,當(dāng)針對(duì)該 Servlet 的訪問(wèn)請(qǐng)求到達(dá)時(shí),web 容器將把這多個(gè) Filter 程序組合成一個(gè) Filter 鏈(過(guò)濾器鏈)。Filter 鏈中各個(gè) Filter 的攔截順序與它們?cè)趹?yīng)用程序的 web.xml 中映射的順序一致
Filter 接口
init(FilterConfig filterConfig)throws ServletException:在 web 應(yīng)用程序啟動(dòng)時(shí),web 服務(wù)器將根據(jù) web.xml 文件中的配置信息來(lái)創(chuàng)建每個(gè)注冊(cè)的 Filter 實(shí)例對(duì)象,并將其保存在服務(wù)器的內(nèi)存中。Web容器創(chuàng)建 Filter 對(duì)象實(shí)例后,將立即調(diào)用該 Filter 對(duì)象的 init 方法。Init 方法在 Filter 生命周期中僅執(zhí)行一次,web 容器在調(diào)用 init 方法時(shí),會(huì)傳遞一個(gè)包含 Filter 的配置和運(yùn)行環(huán)境的 FilterConfig 對(duì)象(FilterConfig的用法和ServletConfig類似)。利用FilterConfig對(duì)象可以得到ServletContext對(duì)象,以及部署描述符中配置的過(guò)濾器的初始化參數(shù)。在這個(gè)方法中,可以拋出ServletException異常,通知容器該過(guò)濾器不能正常工作。
destroy(): 在Web容器卸載 Filter 對(duì)象之前被調(diào)用。該方法在Filter的生命周期中僅執(zhí)行一次。在這個(gè)方法中,可以釋放過(guò)濾器使用的資源。
與開(kāi)發(fā)Servlet不同的是,F(xiàn)ilter接口并沒(méi)有相應(yīng)的實(shí)現(xiàn)類可供繼承,要開(kāi)發(fā)過(guò)濾器,只能直接實(shí)現(xiàn)Filter接口。
doFilter(ServletRequest request,ServletResponse response,
FilterChain chain)throws java.io.IOException,ServletException:
doFilter()方法類似于Servlet接口的service()方法。當(dāng)客戶端請(qǐng)求目標(biāo)資源的時(shí)候,容器就會(huì)調(diào)用與這個(gè)目標(biāo)資源相關(guān)聯(lián)的過(guò)濾器的 doFilter()方法。其中參數(shù) request, response 為 web 容器或 Filter 鏈的上一個(gè) Filter 傳遞過(guò)來(lái)的請(qǐng)求和相應(yīng)對(duì)象;參數(shù) chain 為代表當(dāng)前 Filter 鏈的對(duì)象,在特定的操作完成后,可以在當(dāng)前 Filter 對(duì)象的 doFilter 方法內(nèi)部需要調(diào)用 FilterChain 對(duì)象的 chain.doFilter(request,response)方法才能把請(qǐng)求交付給 Filter 鏈中的下一個(gè) Filter 或者目標(biāo) Servlet 程序去處理,也可以直接向客戶端返回響應(yīng)信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法將請(qǐng)求轉(zhuǎn)向到其他資源。這個(gè)方法的請(qǐng)求和響應(yīng)參數(shù)的類型是 ServletRequest和ServletResponse,也就是說(shuō),過(guò)濾器的使用并不依賴于具體的協(xié)議。
FilterChain 接口:
FilterChain接口:代表當(dāng)前 Filter 鏈的對(duì)象。由容器實(shí)現(xiàn),容器將其實(shí)例作為參數(shù)傳入過(guò)濾器對(duì)象的doFilter()方法中。過(guò)濾器對(duì)象使用FilterChain對(duì)象調(diào)用過(guò)濾器鏈中的下一個(gè)過(guò)濾器,如果該過(guò)濾器是鏈中最后一個(gè)過(guò)濾器,那么將調(diào)用目標(biāo)資源。
doFilter(ServletRequest request,ServletResponse response)throws java.io.IOException:調(diào)用該方法將使過(guò)濾器鏈中的下一個(gè)過(guò)濾器被調(diào)用。如果是最后一個(gè)過(guò)濾器,會(huì)調(diào)用目標(biāo)資源。
在實(shí)現(xiàn)一個(gè)過(guò)濾器后,需要在 web.xml 中進(jìn)行注冊(cè)和設(shè)置它所能攔截的資源。這可以通過(guò)<filter>和<filter-mapping>元素來(lái)完成的。
其配置方式和servlet非常類似,下面是具體的配置代碼
<filter>
<filter-name>testFilterConfig</filter-name>
<filter-class>cn.itcast.filter.TestFilterConfigFilter</filter-class>
<!-- 配置當(dāng)前 Filter 的初始化參數(shù) -->
<init-param>
<param-name>name</param-name>
<param-value>Tom</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>testFilterConfig</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/* 表示所有的url都需要被這個(gè)過(guò)濾器所過(guò)濾
在同一個(gè) web.xml 文件中可以為同一個(gè) Filter 設(shè)置多個(gè)映射。若一個(gè) Filter 鏈中多次出現(xiàn)了同一個(gè) Filter 程序,這個(gè) Filter 程序的攔截處理過(guò)程將被多次執(zhí)行
Filter的典型應(yīng)用
使瀏覽器不緩存頁(yè)面的過(guò)濾器:
有 3 個(gè) HTTP 響應(yīng)頭字段都可以禁止瀏覽器緩存當(dāng)前頁(yè)面,它們?cè)?Servlet 中的示例代碼如下:
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
并不是所有的瀏覽器都能完全支持上面的三個(gè)響應(yīng)頭,因此最好是同時(shí)使用上面的三個(gè)響應(yīng)頭
典型應(yīng)用2
字符編碼的過(guò)濾器
通過(guò)配置參數(shù)encoding指明使用何種字符編碼,以處理Html Form請(qǐng)求參數(shù)的中文問(wèn)題
具體的實(shí)例代碼如下:
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
arg0.setCharacterEncoding(“UTF-8”);
arg2.doFilter(arg0, arg1);
}
如何在src目下的代碼中獲得當(dāng)前web應(yīng)用下的文件?
答:可以定義一個(gè)servlet,這個(gè)servlet不需要映射,將其配置為在服務(wù)器啟動(dòng)時(shí)就啟動(dòng),在這個(gè) servlet中可以獲得 ServletContext對(duì)象,利用這個(gè)對(duì)象就可以獲得我們感興趣的文件的getRealPath(絕對(duì)路徑),把這個(gè)絕對(duì)路徑保存到我們自定義的一個(gè)類的靜態(tài)變量中就可以了。
如果實(shí)現(xiàn)對(duì)客戶端輸入信息的過(guò)濾?
答:使用過(guò)濾器,但是由于request只能獲得頁(yè)面參數(shù),但是卻無(wú)法修改頁(yè)面參數(shù)的值并返回給頁(yè)面(也就是說(shuō):只有g(shù)etParameter方法,但是沒(méi)有setParameter方法),這個(gè)時(shí)候我們只能通過(guò)繼承的方式重寫request對(duì)象的getParameter方法來(lái)實(shí)現(xiàn)。這個(gè)時(shí)候我們提供兩種比較常見(jiàn)的方法來(lái)實(shí)現(xiàn)。
方案1.
繼承 javax.servlet.http.HttpServletRequestWrapper這個(gè)類
這個(gè)類實(shí)際上是對(duì)request對(duì)象的裝飾,通過(guò)構(gòu)造方法獲得一個(gè)request對(duì)象,其內(nèi)部所有HttpServletRequest接口中的方法都是實(shí)際調(diào)用這個(gè)request對(duì)象來(lái)實(shí)現(xiàn)的。
使用這個(gè)類,傳入request對(duì)象,我們就只需要對(duì)我們感興趣的方法進(jìn)行重寫,而其他方法則使用request的默認(rèn)實(shí)現(xiàn)
比如這里我們對(duì)其中的 getParameter方法進(jìn)行重寫
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if(value != null&&value.contains("bad")){
value = value.replaceAll("bad", "=======");
}
return value;
}
方案2:
使用動(dòng)態(tài)代理,傳入一個(gè)request對(duì)象作為需要被代理的對(duì)象,利用動(dòng)態(tài)代理的invoke方法遍歷其內(nèi)部的每個(gè)方法,如果發(fā)現(xiàn)有名為“getParameter”,就進(jìn)行覆蓋,其他方法則直接調(diào)用其最初的實(shí)現(xiàn)。
1. 區(qū)別幾個(gè)用語(yǔ):
假設(shè)工程為filter
2.web站點(diǎn)的根目錄(路徑)
web站點(diǎn)的根目錄: http://localhost:8080/
3.、web應(yīng)用的的根目錄、
http://localhost:8080/filter/
4. 當(dāng)前目錄
當(dāng)前目錄(同一目錄下的不同文件): http://localhost:8080/filter/mydir/
2."/" 的意義:(工程為filter)
一、在-form-表單的action屬性中代表web站點(diǎn)的根目錄
action="/loginservlet" 代表http://localhost:8080/loginservlet 服務(wù)器會(huì)去當(dāng)前web應(yīng)用根目錄的外面去,這肯定會(huì)出錯(cuò)!
action="loginservlet" 代表http://localhost:8080/filter/loginservlet 這是我們一般映射Servlet的路徑(要求表單與loginservlet在同一級(jí)目錄下)
二、在--重定向--操作中代表web站點(diǎn)的根目錄
在LoginServlet.java中:
response.sendRedirect("/hello.jsp"); 代表http://localhost:8080/hello.jsp 不存在,會(huì)報(bào)錯(cuò)response.sendRedirect("hello.jsp"); 代表hello.jsp與loginservlet在同一目錄下,否則會(huì)報(bào)錯(cuò)
三、在--轉(zhuǎn)發(fā)--操作中代表當(dāng)前web應(yīng)用的的根目錄
在LoginServlet.java中:
request.getRequestDispatcher("/hello.jsp") 代表http://localhost:8080/filter/hello.jsp
request.getRequestDispatcher("hello.jsp") 代表hello.jsp與loginservlet在同一目錄下
四、在--映射路徑--操作中代表當(dāng)前web應(yīng)用的的根目錄
在web.xml文件中:
<url-pattern>/loginservlet</url-pattern> 代表將LoginServlet映射成:http://localhost:8080/filter/LoginServlet
五、無(wú)"/"則代表當(dāng)前目錄
要求發(fā)送請(qǐng)求的對(duì)象與接受請(qǐng)求的對(duì)象在同一目錄下,否則就會(huì)報(bào)錯(cuò)
請(qǐng)測(cè)試一個(gè)例子:
在LonginServlet.java中
request.getRequestDispatcher(servletContext.getContextPath()+"/login.jsp") 是轉(zhuǎn)發(fā)到的url為:http://localhost:8080/filter/login.jsp 嗎?
答:這個(gè)是錯(cuò)誤的,轉(zhuǎn)發(fā)的時(shí)候,“/login.jsp”中的“/” 已經(jīng)代表當(dāng)前web應(yīng)用的根了,如果再加上 servletContext.getContextPath(),最后拼接出來(lái)的url是:
/filter/filter /-----,弄出兩個(gè)filter,肯定出錯(cuò)。
Filter的一些補(bǔ)充
映射 Filter的<dispatcher> 子元素可以設(shè)置的值及其意義
REQUEST:當(dāng)用戶直接訪問(wèn)頁(yè)面時(shí),Web容器將會(huì)調(diào)用過(guò)濾器。如果目標(biāo)資源是通過(guò)RequestDispatcher的include()或forward()方法訪問(wèn)時(shí),那么該過(guò)濾器就不會(huì)被調(diào)用。
INCLUDE: 如果目標(biāo)資源是通過(guò)RequestDispatcher的include()方法訪問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用。除此之外,該過(guò)濾器不會(huì)被調(diào)用。
FORWARD: 如果目標(biāo)資源是通過(guò)RequestDispatcher的forward()方法訪問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用,除此之外,該過(guò)濾器不會(huì)被調(diào)用。
ERROR: 如果目標(biāo)資源是通過(guò)聲明式異常處理機(jī)制調(diào)用時(shí),那么該過(guò)濾器將被調(diào)用。除此之外,過(guò)濾器不會(huì)被調(diào)用。
如果不進(jìn)行配置,默認(rèn)是只過(guò)濾REQUEST。
當(dāng)同時(shí)配置了好幾個(gè)過(guò)濾相同目標(biāo)的filter的時(shí)候,要注意他們的執(zhí)行順序,下圖反應(yīng)了這個(gè)順序。
Servlet 監(jiān)聽(tīng)器
監(jiān)聽(tīng)器:專門用于對(duì)其他對(duì)象身上發(fā)生的事件或狀態(tài)改變進(jìn)行監(jiān)聽(tīng)和相應(yīng)處理的對(duì)象,當(dāng)被監(jiān)視的對(duì)象發(fā)生情況時(shí),立即采取相應(yīng)的行動(dòng)。
Servlet 監(jiān)聽(tīng)器:Servlet 規(guī)范中定義的一種特殊類,它用于監(jiān)聽(tīng) web 應(yīng)用程序中的 ServletContext, HttpSession 和 ServletRequest 等域?qū)ο蟮膭?chuàng)建與銷毀事件,以及監(jiān)聽(tīng)這些域?qū)ο笾械膶傩园l(fā)生修改的事件。
ServletContextListener 接口
ServletContextListener 接口用于監(jiān)聽(tīng) ServletContext 對(duì)象的創(chuàng)建和銷毀事件。
當(dāng) ServletContext 對(duì)象被創(chuàng)建時(shí),激發(fā)contextInitialized (ServletContextEvent sce)方法
當(dāng) ServletContext 對(duì)象被銷毀時(shí),激發(fā)contextDestroyed(ServletContextEvent sce)方法
HttpSessionListener 接口
HttpSessionListener 接口用于監(jiān)聽(tīng)HttpSession對(duì)象的創(chuàng)建和銷毀
創(chuàng)建一個(gè) Session時(shí),激發(fā)sessionCreated
(HttpSessionEvent se) 方法
銷毀一個(gè)Session時(shí),激發(fā)sessionDestroyed (HttpSessionEvent se) 方法。
ServletRequestListener 接口
ServletRequestListener 接口用于監(jiān)聽(tīng)ServletRequest 對(duì)象的創(chuàng)建和銷毀
創(chuàng)建一個(gè)ServletRequest 對(duì)象時(shí),激發(fā)requestInitialized(ServletRequestEvent sre)方法
銷毀一個(gè)Session時(shí),激發(fā)requestDestroyed(ServletRequestEvent sre)方法。
HttpSessionBindingListener 接口
實(shí)現(xiàn)了HttpSessionBindingListener接口的 JavaBean 對(duì)象可以感知自己被綁定到 Session 中和從 Session 中刪除的事件
當(dāng)對(duì)象被綁定到 HttpSession 對(duì)象中時(shí),web 服務(wù)器調(diào)用該對(duì)象的 void valueBound(HttpSessionBindingEvent event) 方法
當(dāng)對(duì)象從 HttpSession 對(duì)象中解除綁定時(shí),web 服務(wù)器調(diào)用該對(duì)象的 void valueUnbound(HttpSessionBindingEvent event)方法
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。