你能夠在兩種狀況下使用本文:
·學習過濾器的功用,
·做爲你寫過濾器時的輔助。
我將從幾個簡單的例子開始而後繼續更多高級的過濾器。最後,我將向你介紹我爲了支持多路請求而寫的一個文件上傳過濾器。
Servlet 過濾器
也許你還不熟悉狀況,一個過濾器是一個能夠傳送請求或修改響應的對象。過濾器並非
servlet,他們並不實際建立一個請求。他們是請求到達一個servlet前的預處理程序,和/或響應離開servlet後的後處理程序。就像你將在後面的例子中看到的,一個過濾器可以:
·在一個
servlet被調用前截獲該調用
·在一個
servlet被調用前檢查請求
·修改在實際請求中提供了可定製請求對象的請求頭和請求數據
·修改在實際響應中提供了可定製響應對象的響應頭和響應數據
·在一個
servlet被調用以後截獲該調用
一個過濾器以做用於一個或一組servlet,零個或多個過濾器能過濾一個或多個servlet。一個過濾器須要實現java.servlet.Filter接口,並定義它的三個方法:
1.
void init(FilterConfig config) throws ServletException:在過濾器執行service前被調用,以設置過濾器的配置對象。
2.
void destroy();在過濾器執行service後被調用。
3.
Void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws IOException,ServletException;執行實際的過濾工做。
服務器調用一次
init(FilterConfig)覺得服務準備過濾器,而後在請求須要使用過濾器的任什麼時候候調用doFilter()。FilterConfig接口檢索過濾器名、初始化參數以及活動的servlet上下文。服務器調用destory()以指出過濾器已結束服務。過濾器的生命週期和servelt的生命週期很是類似 ——在Servlet API 2.3 最終發佈稿2號 中最近改變的。先前得用setFilterConfig(FilterConfig)方法來設置生命週期。
在
doFilter()方法中,每一個過濾器都接受當前的請求和響應,而FilterChain包含的過濾器則仍然必須被處理。doFilter()方法中,過濾器能夠對請求和響應作它想作的一切。(就如我將在後面討論的那樣,經過調用他們的方法收集數據,或者給對象添加新的行爲。)過濾器調用
chain.doFilter()將控制權傳送給下一個過濾器。當這個調用返回後,過濾器能夠在它的doFilter()方法的最後對響應作些其餘的工做;例如,它能記錄響應的信息。若是過濾器想要終止請求的處理或得對響應的徹底控制,則他能夠不調用下一個過濾器。
按部就班
若是想要真正理解過濾器,則應該看它們在實際中的應用。咱們將看到的第一個過濾器是簡單而有用的,它記錄了全部請求的持續時間。在
Tomcat 4.0發佈中被命名爲ExampleFilter。代碼以下:
java 代碼
- import java.io.*;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class TimerFilter implements Filter {
-
- private FilterConfig config = null;
-
- public void init(FilterConfig config) throws ServletException {
- this.config = config;
- }
-
- public void destroy() {
- config = null;
- }
-
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- long before = System.currentTimeMillis();
- chain.doFilter(request, response);
- long after = System.currentTimeMillis();
-
- String name = "";
- if (request instanceof HttpServletRequest) {
- name = ((HttpServletRequest)request).getRequestURI();
- }
- config.getServletContext().log(name + ": " + (after - before) + "ms");
- }
- }
當服務器調用
init()時,過濾器用config變量來保存配置類的引用,這將在後面的doFilter()方法中被使用以更改ServletContext。當調用doFilter()時,過濾器計算請求發出到該請求執行完畢之間的時間。該過濾器很好的演示了請求以前和以後的處理。注意doFilter()方法的參數並非HTTP對象,所以要調用HTTP專用的getRequestURI()方法時必須將request轉化爲HttpServletRequest類型。
使用此過濾器,你還必須在
web.xml文件中用<filter></filter>標籤部署它,見下:
- <filter>
- <filter-name>timerFilterfilter-name>
- <filter-class>TimerFilterfilter-class>
- /filter>
這將通知服務器一個叫
timerFiter的過濾器是從TimerFiter類實現的。你可使用肯定的URL模式或使用<filter-mapping></filter-mapping>標籤命名的servelt 來註冊一個過濾器,如:
- <filter-mapping>
- <filter-name>timerFilterfilter-name>
- <url-pattern>/*url-pattern>
- filter-mapping>
這種配置使過濾器操做全部對服務器的請求(靜態或動態),正是咱們須要的計時過濾器。若是你鏈接一個簡單的頁面,記錄輸出可能以下:
2001-05-25 00:14:11 /timer/index.html: 10ms
在
Tomcat 4.0 beta 5中,你能夠在server_root/logs/下找到該記錄文件。
此過濾器的
WAR文件今後下載:
誰在你的網站上?他們在作什麼?
咱們下一個過濾器是由
OpenSymphony成員寫的clickstream過濾器。這個過濾器跟蹤用戶請求(好比:點擊)和請求隊列(好比:點擊流)以向網絡管理員顯示誰在她的網站上以及每一個用戶正在訪問那個頁面。這是個使用LGPL的開源庫。
在
clickstream包中你將發現一個捕獲請求信息的ClickstreamFilter類,一個像操做結構同樣的Clickstream類以保存數據,以及一個保存會話和上下文事件的ClickstreamLogger類以將全部東西組合在一塊兒。還有個BotChecker類用來肯定客戶端是不是一個機器人(簡單的邏輯,像「他們是不是從robots.txt來的請求?」)。該包中提供了一個clickstreams.jsp摘要頁面和一個viewstream.jsp詳細頁面來查看數據。
咱們先看
ClickstreamFilter類。全部的這些例子都作了些輕微的修改以格式化並修改了些可移植性問題,這我將在後面將到。
- import java.io.IOException;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class ClickstreamFilter implements Filter {
- protected FilterConfig filterConfig;
- private final static String FILTER_APPLIED = "_clickstream_filter_applied";
-
- public void init(FilterConfig config) throws ServletException {
- this.filterConfig = filterConfig;
- }
-
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- // 確保該過濾器在每次請求中只被使用一次
- if (request.getAttribute(FILTER_APPLIED) == null) {
- request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
- HttpSession session = ((HttpServletRequest)request).getSession();
- Clickstream stream = (Clickstream)session.getAttribute("clickstream");
- stream.addRequest(((HttpServletRequest)request));
- }
-
- // 傳遞請求
- chain.doFilter(request, response);
- }
-
- public void destroy() { }
- }
doFilter()方法取得用戶的session,從中獲取Clickstream,並將當前請求數據加到Clickstream中。其中使用了一個特殊的FILTER_APPLIED標記屬性來標註此過濾器是否已經被當前請求使用(可能會在請求調度中發生)而且忽略全部其餘的過濾器行爲。你可能疑惑過濾器是怎麼知道當前session中有clickstream屬性。那是由於ClickstreamLogger在會話一開始時就已經設置了它。ClickstreamLogger代碼:
- import java.util.*;
- import javax.servlet.*;
- import javax.servlet.http.*;
-
- public class ClickstreamLogger implements ServletContextListener,
- HttpSessionListener {
- Map clickstreams = new HashMap();
-
- public ClickstreamLogger() { }
-
- public void contextInitialized(ServletContextEvent sce) {
- sce.getServletContext().setAttribute("clickstreams", clickstreams);
- }
-
- public void contextDestroyed(ServletContextEvent sce) {
- sce.getServletContext().setAttribute("clickstreams", null);
- }
-
- public void sessionCreated(HttpSessionEvent hse) {
- HttpSession session = hse.getSession();
- Clickstream clickstream = new Clickstream();
- session.setAttribute("clickstream", clickstream);
- clickstreams.put(session.getId(), clickstream);
- }
-
- public void sessionDestroyed(HttpSessionEvent hse) {
- HttpSession session = hse.getSession();
- Clickstream stream = (Clickstream)session.getAttribute("clickstream");
- clickstreams.remove(session.getId());
- }
- }
logger(記錄器)獲取應用事件並將使用他們將全部東西幫定在一塊兒。當context建立中,logger在context中放置了一個共享的流map。這使得clickstream.jsp頁面知道當前活動的是哪一個流。而在context銷燬中,logger則移除此map。當一個新訪問者建立一個新的會話時,logger將一個新的Clickstream實例放入此會話中並將此Clickstream加入到中心流map中。在會話銷燬時,由logger從中心map中移除這個流。
下面的
web.xml部署描述片斷將全部東西寫在一塊:
- <filter>
- <filter-name>clickstreamFilterfilter-name>
- <filter-class>ClickstreamFilterfilter-class>
- filter>
- <filter-mapping>
- <filter-name>clickstreamFilterfilter-name>
- <url-pattern>*.jspurl-pattern>
- filter-mapping>
- <filter-mapping>
- <filter-name>clickstreamFilterfilter-name>
- <url-pattern>*.htmlurl-pattern>
- filter-mapping>
- <listener>
- <listener-class>ClickstreamLoggerlistener-class>
- listener>