1. 程式人生 > >Filter的執行順序與實例

Filter的執行順序與實例

style 訪問 用處 path n) @override 足夠 log 註意

Filter介紹

Filter可認為是Servlet的一種“變種”,它主要用於對用戶請求進行預處理,也可以對HttpServletResponse進行後處理,是個典型的處理鏈。它與Servlet的區別在於:它不能直接向用戶生成響應。完整的流程是:Filter對用戶請求進行預處理,接著將請求交給Servlet進行處理並生成響應,最後Filter再對服務器響應進行後處理。

Filter有如下幾個用處。

  • 在HttpServletRequest到達Servlet之前,攔截客戶的HttpServletRequest。
  • 根據需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和數據。
  • 在HttpServletResponse到達客戶端之前,攔截HttpServletResponse。
  • 根據需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和數據。

Filter有如下幾個種類。

  • 用戶授權的Filter:Filter負責檢查用戶請求,根據請求過濾用戶非法請求。
  • 日誌Filter:詳細記錄某些特殊的用戶請求。
  • 負責解碼的Filter:包括對非標準編碼的請求解碼。
  • 能改變XML內容的XSLT Filter等。
  • Filter可負責攔截多個請求或響應;一個請求或響應也可被多個請求攔截。

創建一個Filter只需兩個步驟:

  • 建Filter處理類;
  • web.xml文件中配置Filter。

下面先介紹一個簡單的記錄日誌的Filter,這個Filter負責攔截所有的用戶請求,並將請求的信息記錄在日誌中。

public class LogFilter implements Filter {

    /**
     * FilterConfig可用於訪問Filter的配置信息
     */
    private FilterConfig config;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
this.config = config; } /** * 執行過濾的核心方法 * * @param request * @param response * @param chain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //---------下面代碼用於對用戶請求執行預處理--------- //獲取ServletContext對象,用於記錄日誌 ServletContext context = this.config.getServletContext(); long before = System.currentTimeMillis(); System.out.println("開始過濾..."); //將請求轉換成HttpServletRequest請求 HttpServletRequest hrequest = (HttpServletRequest) request; //記錄日誌 context.log("Filter已經截獲到用戶的請求地址: " + hrequest.getServletPath()); //Filter只是鏈式處理,請求依然放行到目的地址 chain.doFilter(request, response); //---------下面代碼用於對服務器響應執行後處理--------- long after = System.currentTimeMillis(); //記錄日誌 context.log("過濾結束"); //再次記錄日誌 context.log("請求被定位到" + hrequest.getRequestURI() + "所花的時間為: " + (after - before)); } @Override public void destroy() { this.config = null; } }

上面程序實現了doFilter()方法,實現該方法就可實現對用戶請求進行預處理,也可實現對服務器響應進行後處理——它們的分界線為是否調用了chain.doFilter(),執行該方法之前,即對用戶請求進行預處理;執行該方法之後,即對服務器響應進行後處理。

在上面的請求Filter中,僅在日誌中記錄請求的URL,對所有的請求都執行chain.doFilter (request,reponse)方法,當Filter對請求過濾後,依然將請求發送到目的地址。如果需要檢查權限,可以在Filter中根據用戶請求的HttpSession,判斷用戶權限是否足夠。如果權限不夠,直接調用重定向即可,無須調用chain.doFilter(request,reponse)方法。

FirstFilter.java

public class FirstFilter implements Filter {

    @Override
    public void destroy() {
        System.out.println("1st destroy()...");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        System.out.println("before invoke 1stFilter‘s chain.doFilter() ..");
        chain.doFilter(request, response);
        System.out.println("after invoke 1stFilter‘s chain.doFilter() ..");
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        System.out.println("1stFilter init()...");
    }
}

SecondFilter.java

public class SecondFilter implements Filter {

    @Override
    public void destroy() {
        System.out.println("2ndFilter destroy()...");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        System.out.println("before invoke 2ndFilter‘s chain.doFilter() ..");
        chain.doFilter(request, response);
        System.out.println("after invoke 2ndFilter‘s chain.doFilter() ..");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("2ndFilter init()...");
    }
}

MyServlet.java

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("servlet doGet be invoked...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.doGet(req, resp);
    }
}

web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <filter>
        <filter-name>firstFilter</filter-name>
        <filter-class>com.winner.FirstFilter</filter-class>
    </filter>
    <filter>
        <filter-name>secondFilter</filter-name>
        <filter-class>com.winner.SecondFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>secondFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.winner.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>
</web-app>

然後發布,發現打印的日誌如下:

1stFilter init()...
2ndFilter init()...

這裏過濾器初始化好了。

當我們訪問我們的 應用:http://localhost:8080/MyServlet

before invoke 2ndFilter‘s chain.doFilter() ..
before invoke 1stFilter‘s chain.doFilter() ..
servlet doGet be invoked...
after invoke 1stFilter‘s chain.doFilter() ..
after invoke 2ndFilter‘s chain.doFilter() ..

當我們將web.xml中filter的位置進行調整後(註意filter-mapping的順序):

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <filter>
        <filter-name>firstFilter</filter-name>
        <filter-class>com.winner.FirstFilter</filter-class>
    </filter>
    <filter>
        <filter-name>secondFilter</filter-name>
        <filter-class>com.winner.SecondFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>secondFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.winner.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>
</web-app>

當我們訪問我們的 應用:http://localhost:8080/MyServlet

before invoke 1stFilter‘s chain.doFilter() ..
before invoke 2ndFilter‘s chain.doFilter() ..
servlet doGet be invoked...
after invoke 2ndFilter‘s chain.doFilter() ..
after invoke 1stFilter‘s chain.doFilter() ..

技術分享圖片

Filter的執行順序與實例