Java Servlet 過濾器與 springmvc 攔截器的區別?

前言:在工做中,遇到須要記錄日誌的狀況,不知道該選擇過濾器仍是攔截器,故總結了一下。css

servlet 過濾器

定義

  java過濾器可以對目標資源的請求和響應進行截取。過濾器的工做方式分爲四種html

應用場景

  能夠經過 doFilter 方法的 request、response 提早過濾一些不想要的信息,統一設置一些參數、統一設置字符集、控制權限是否登陸等。java

配置  

  <!-- 定義Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>loginFilter</filter-name>
    <!-- Filter的實現類 -->
    <filter-class>com.yule.common.filters.LoginFilter</filter-class>
  </filter>
  <!-- 定義Filter攔截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>loginFilter</filter-name>
    <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

過濾器的4種工做方式

<filter-mapping>
    <filter-name>myFilter</filter-name>
    <servlet-name>目標資源</servlet-name>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

  四中工做方式經過配置 <dispatcher> 標籤來決定web

  1. request 過濾器:不配 <dispatcher> 標籤,或者配置爲 <dispatcher>REQUEST</dispatcher> 。說明只有直接訪問該目標資源時該過濾器纔會起做用,對轉發到該目標資源的請求將忽略不處理。
  2. forward 過濾器:配置爲 <dispatcher>FORWARD</dispatcher> 。表示對轉發到目標資源的請求過濾,若是直接訪問目標資源,過濾器則不起做用。
  3. include 過濾器:配置爲 <dispatcher>INCLUDE</dispatcher> 。表示對包含了目標資源的請求過濾,若是直接訪問目標資源,則此過濾器將不起做用
    include 包含如下語句:
    在 JSP 頁面中的動做:<jsp:include page=.......
    在 Java 代碼中的 request.getRequestDispatcher("....").include
    注意:若是目標資源一經過 <%@ include file="目標資源二"%> 指令包含,這時此過濾器不工做,由於這個是指令,在JSP 編譯時插入一個包含文本或代碼的文件,這個包含的過程是靜態的。
  4. error 過濾器:配置爲
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/error.jsp</url-pattern>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>
    
    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>
    當咱們訪問一個web目標資源時,若是服務器沒有找到該目標資源,那麼服務器就會給出一個404錯誤代碼。若是咱們給404錯誤代碼定義一個頁面,那麼當404發生時就會調用該頁面。
    當咱們訪問一個不存在的文件時,就會訪問error.jsp,可是配置了過濾器對錯誤頁面進行過濾,因此過濾器先接受到請求,而後再轉發給error.jsp。
    若是咱們訪問一個已經存在的頁面,會不會調用error.jsp呢?若是這個頁面中有response.sendError(404,"出錯了!");那麼該錯誤頁面仍然會被調用,過濾器也會工做。

執行順序

  根據 web.xml 的代碼順序來決定過濾器的執行順序。Filter 鏈: 一個Web應用中,能夠編寫多個Filter,這些 Filter 組合起來稱之爲一個Filter鏈。spring

  當第一個 Filter 的 doFilter 方法被調用時,web 服務器會建立一個表明 Filter 鏈的 FilterChain 對象傳遞給該方法。在 doFilter 方法中,若是調用了 FilterChain 對象的 doFilter 方法,則 web 服務器會檢 FilterChain 對象中是否還有 filter ,若是有,則調用第下一個 filter,若是沒有,則調用目標資源。數據庫

  init() 方法和 destroy() 方法隨着項目的啓動和關閉纔會被調用,且僅一次。編程

舉個栗子

  web.xml 中spring-mvc

<!-- 定義Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter的實現類 -->
    <filter-class>com.yule.common.filters.DemoFilter</filter-class>
  </filter>
  <!-- 定義Filter攔截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  Java 代碼服務器

package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * 過濾器
 * @author yule
 * @date 2018/7/2 21:52
 */
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo過濾器init。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo過濾器doFilter。。。此處省略業務處理邏輯");

        //經過判斷是否繼續往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo過濾器destroy。。。");
    }
}

 

springmvc 攔截器

定義

  springMVC 攔截器源碼解析cookie

  Spring Web MVC的處理器攔截器。相似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。攔截器是面向切面編程的,依賴的技術就是Java的動態代理。

應用場景

  1. 日誌記錄:記錄請求日誌等
  2. 權限檢查:白名單等
  3. 性能監控:能夠經過攔截器在進入處理器以前記錄開始時間,在處理完後記錄結束時間,從而獲得該請求的處理時間;
  4. 通用行爲:讀取cookie獲得用戶信息並將用戶對象放入請求,從而方便後續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都須要的便可使用攔截器實現。
  5. OpenSessionInView:如Hibernate,在進入處理器打開Session,在完成後關閉Session。

  本質是AOP(面向切面編程),符合 AOP 的全部功能均可以使用攔截器實現。

配置

  在 spring-mvc.xml 中

<mvc:interceptors>
        <!--  使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--進行攔截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

執行順序

  根據 xml 中的配置順序來執行。攔截器的執行順序在過濾器之間。

方法說明

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,該法在請求處理以前進行調用。SpringMVC 中的 Interceptor 是鏈式調用的,在一個應用中或者說是在一個請求中能夠同時存在多個 Interceptor 。每一個 Interceptor 的調用會依據它的聲明順序依次執行,並且最早執行的都是 Interceptor 中的 preHandle 方法,因此能夠在這個方法中進行一些前置初始化操做或者是對當前請求作一個預處理,也能夠在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值 Boolean 類型的,當它返回爲 false 時,表示請求結束,後續的 Interceptor 和 Controller 都不會再執行;當返回值爲 true 時,就會繼續調用下一個 Interceptor 的 preHandle 方法,若是已是最後一個 Interceptor 的時候,就會是調用當前請求的 Controller 中的方法。
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,經過 preHandle 方法的解釋我們知道這個方法包括後面要說到的 afterCompletion 方法都只能在當前所屬的 Interceptor 的 preHandle 方法的返回值爲 true 的時候,才能被調用。postHandle 方法在當前請求進行處理以後,也就是在 Controller 中的方法調用以後執行,可是它會在 DispatcherServlet 進行視圖返回渲染以前被調用,因此我們能夠在這個方法中對 Controller 處理以後的 ModelAndView 對象進行操做。postHandle 方法被調用的方向跟 preHandle 是相反的,也就是說,先聲明的 Interceptor 的 postHandle 方法反而會後執行。
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,也是須要當前對應的 Interceptor 的 preHandle 方法的返回值爲 true 時纔會執行。所以,該方法將在整個請求結束以後,也就是在 DispatcherServlet 渲染了對應的視圖以後執行,這個方法的主要做用是用於進行資源清理的工做。afterCompletion 方法被調用的方向也跟 preHandle 是相反的,也就是說,先聲明的 Interceptor 的 afterCompletion 方法反而會後執行

舉個栗子

  spring-mvc 中

<mvc:interceptors>
        <!--  使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--進行攔截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

  Java 代碼

package com.yule.common.interceptors;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定義攔截器方式一
 * Created by yule on 2018/7/2 22:37.
 */
public class DemoInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //return true 表示繼續下一個攔截器或者 control 層
        //return false 表示被攔截下來
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
package com.yule.common.interceptors;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定義攔截器方式二
 * 通常都是經過實現HandlerInterceptor接口或者繼承HandlerInterceptorAdapter抽象類,複寫preHandle()、postHandle()和afterCompletion()這 3 個方法來對用戶的請求進行攔截處理
 * Created by yule on 2018/7/2 22:43.
 */
public class Demo2Interceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
}

 區別

  其實二者仍是有類似之處,就是均可以用做權限檢查、日誌記錄等狀況,可是在這些狀況下如何選擇就要知道不一樣之處。

不一樣之處

  使用範圍不一樣:Filter 只能用於 Web 程序中。而攔截器能夠用於 Web 程序,Application、Swing 程序中。

  規範不一樣:Filter 是 servlet 規範規定的,是 servlet 支持的。而攔截器是在 spring 容器內,是 spring 框架支持的。

  使用資源不一樣:Filter 不能直接使用 spring 的資源、對象等。而攔截器是一個 spring 組件,歸 spring 管理,配置在 spring 文件中,所以能使用 spring 的任何資源、對象,例如 Service 對象、數據源、事務管理等,經過 IoC 注入到攔截器便可。也就是說在攔截器中能夠注入一個 service ,用於業務邏輯或者訪問數據庫。

  深度不一樣:Filter 只在 Servlet 先後起做用。而攔截器可以深刻到方法先後、異常拋出先後等,所以攔截器的使用具備更大的彈性。

  做用範圍不一樣:攔截器只能對 Controller 層請求起做用,而過濾器則能夠對幾乎全部的請求起做用(如 .js、.css等)。

  因此,在 Spring 構架的程序中,要優先使用攔截器。

注意

  攔截器是在過濾器之間運行的。

執行順序舉例

攔截器,spring-mvc.xml 中:

<mvc:interceptors>
        <!--  使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--進行攔截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

        <mvc:interceptor>
            <!--進行攔截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.Demo2Interceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

java 代碼:

package com.yule.common.interceptors;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定義攔截器方式一
 * Created by yule on 2018/7/2 22:37.
 */
public class DemoInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //return true 表示繼續下一個攔截器或者 control 層
        //return false 表示被攔截下來
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion");
    }
}
View Code
package com.yule.common.interceptors;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定義攔截器方式二
 * 通常都是經過實現HandlerInterceptor接口或者繼承HandlerInterceptorAdapter抽象類,複寫preHandle()、postHandle()和afterCompletion()這 3 個方法來對用戶的請求進行攔截處理
 * Created by yule on 2018/7/2 22:43.
 */
public class Demo2Interceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle 2222222...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle 22222222");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion 2222222");
    }
}
View Code

過濾器, web.xml 中:

<!-- 定義Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter的實現類 -->
    <filter-class>com.yule.common.filters.DemoFilter</filter-class>
  </filter>
  <!-- 定義Filter攔截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>demo2Filter</filter-name>
    <filter-class>com.yule.common.filters.Demo2Filter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>demo2Filter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

java 代碼:

package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * 過濾器
 * @author yule
 * @date 2018/7/2 21:52
 */
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo過濾器init。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo過濾器doFilter。。。此處省略業務處理邏輯");

        //經過判斷是否繼續往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo過濾器destroy。。。");
    }
}
View Code
package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * Created by yule on 2018/7/2 22:18.
 */
public class Demo2Filter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo2過濾器init  2222222");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo過濾器doFilter  222222");

        //經過判斷是否繼續往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo2過濾器destroy  22222 ");
    }
}
View Code

調用 controller 打印結果:

相關文章
相關標籤/搜索