SpringMVC的攔截器(Interceptor)和過濾器(Filter)的區別與聯系


https://blog.csdn.net/xiaoyaotan_111/article/details/53817918

一 簡介

(1)過濾器:

依賴於servlet容器。在實現上基於函數回調,可以對幾乎所有請求進行過濾,但是缺點是一個過濾器實例只能在容器初始化時調用一次。使用過濾器的目的是用來做一些過濾操作,獲取我們想要獲取的數據,比如:在過濾器中修改字符編碼;在過濾器中修改HttpServletRequest的一些參數,包括:過濾低俗文字、危險字符等

關於過濾器的一些用法可以參考我寫過的這些文章

  • 繼承HttpServletRequestWrapper以實現在Filter中修改HttpServletRequest的參數:https://www.zifangsky.cn/677.html

  • 在SpringMVC中使用過濾器(Filter)過濾容易引發XSS的危險字符:https://www.zifangsky.cn/683.html

(2)攔截器:

依賴於web框架,在SpringMVC中就是依賴於SpringMVC框架。在實現上基於Java的反射機制,屬於面向切面編程(AOP)的一種運用。由於攔截器是基於web框架的調用,因此可以使用Spring的依賴注入(DI)進行一些業務操作,同時一個攔截器實例在一個controller生命周期之內可以多次調用。但是缺點是只能對controller請求進行攔截,對其他的一些比如直接訪問靜態資源的請求則沒辦法進行攔截處理

關於過濾器的一些用法可以參考我寫過的這些文章:

  • 在SpringMVC中使用攔截器(interceptor)攔截CSRF攻擊(修):https://www.zifangsky.cn/671.html

  • SpringMVC中使用Interceptor+cookie實現在一定天數之內自動登錄:https://www.zifangsky.cn/700.html

二 多個過濾器與攔截器的代碼執行順序

如果在一個項目中僅僅只有一個攔截器或者過濾器,那么我相信相對來說理解起來是比較容易的。但是我們是否思考過:如果一個項目中有多個攔截器或者過濾器,那么它們的執行順序應該是什么樣的?或者再復雜點,一個項目中既有多個攔截器,又有多個過濾器,這時它們的執行順序又是什么樣的呢?

下面我將用簡單的代碼來測試說明:

(1)先定義兩個過濾器:

i)過濾器1:

package cn.zifangsky.filter;
 
import java.io.IOException;
 
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.filter.OncePerRequestFilter;
 
public class TestFilter1 extends OncePerRequestFilter {
 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        //在DispatcherServlet之前執行
        system.out.println("############TestFilter1 doFilterInternal executed############");
        filterChain.doFilter(request, response);
        //在視圖頁面返回給客戶端之前執行,但是執行順序在Interceptor之后
        System.out.println("############TestFilter1 doFilter after############");
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }
 
}

ii)過濾器2:

package cn.zifangsky.filter;
 
import java.io.IOException;
 
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.filter.OncePerRequestFilter;
 
public class TestFilter2 extends OncePerRequestFilter {
 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        System.out.println("############TestFilter2 doFilterInternal executed############");
        filterChain.doFilter(request, response);
        System.out.println("############TestFilter2 doFilter after############");
 
    }
 
}

iii)在web.xml中注冊這兩個過濾器:

    <!-- 自定義過濾器:testFilter1 -->   
   <filter>  
        <filter-name>testFilter1</filter-name>  
        <filter-class>cn.zifangsky.filter.TestFilter1</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>testFilter1</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  
    <!-- 自定義過濾器:testFilter2 -->   
   <filter>  
        <filter-name>testFilter2</filter-name>  
        <filter-class>cn.zifangsky.filter.TestFilter2</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>testFilter2</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  

2)再定義兩個攔截器:

i)攔截器1,基本攔截器:

package cn.zifangsky.interceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class BaseInterceptor implements HandlerInterceptor{
    
    /**
     * 在DispatcherServlet之前執行
     * */
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("************BaseInterceptor preHandle executed**********");
        return true;
    }
 
    /**
     * 在controller執行之后的DispatcherServlet之后執行
     * */
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        System.out.println("************BaseInterceptor postHandle executed**********");
    }
    
    /**
     * 在頁面渲染完成返回給客戶端之前執行
     * */
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("************BaseInterceptor afterCompletion executed**********");
//        Thread.sleep(10000);
    }
 
}

ii)指定controller請求的攔截器:

package cn.zifangsky.interceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class TestInterceptor implements HandlerInterceptor {
 
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("************TestInterceptor preHandle executed**********");
        return true;
    }
 
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        System.out.println("************TestInterceptor postHandle executed**********");
    }
 
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("************TestInterceptor afterCompletion executed**********");
    }
}

iii)在SpringMVC的配置文件中注冊這兩個攔截器:

    <!-- 攔截器 -->
     <mvc:interceptors>
        <!-- 對所有請求都攔截,公共攔截器可以有多個 -->
        <bean name="baseInterceptor" class="cn.zifangsky.interceptor.BaseInterceptor" />
        <!-- <bean name="testInterceptor" class="cn.zifangsky.interceptor.TestInterceptor" /> -->
        <mvc:interceptor>        
            <!-- 對/test.html進行攔截 -->
            <mvc:mapping path="/test.html"/>
            <!-- 特定請求的攔截器只能有一個 -->
            <bean class="cn.zifangsky.interceptor.TestInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

(3)定義一個測試使用的controller:

package cn.zifangsky.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
 
@Controller
public class TestController {
    
    @RequestMapping("/test.html")
    public ModelAndView handleRequest(){
        System.out.println("---------TestController executed--------");
        return new ModelAndView("test");
    }
}

4)視圖頁面test.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>    
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<base href="http://983836259.blog.51cto.com/7311475/">
<title>FilterDemo</title>
</head>
<body>
    <%
        System.out.println("test.jsp is loading");
    %>
    <div align="center">
        This is test page
    </div>
</body>
</html>

5)測試效果:

啟動此測試項目,可以看到控制台中輸出如下:

wKioL1hHhYrRCpQYAABafsYR7go378.png

這就說明了過濾器的運行是依賴於servlet容器的,跟springmvc等框架並沒有關系。並且,多個過濾器的執行順序跟xml文件中定義的先后關系有關

接着清空控制台中的輸出內容並訪問:http://localhost:9180/FilterDemo/test.html

可以看到,此時的控制台輸出結果如下:

wKiom1hHhaPRQuBxAACG4WdOJbM758.png

相信從這個打印輸出,大家就可以很清晰地看到有多個攔截器和過濾器存在時的整個執行順序了。當然,對於過個攔截器它們之間的執行順序跟在SpringMVC的配置文件中定義的先后順序有關

注:對於整個SpringMVC的執行流程來說,如果加上上面的攔截器和過濾器,其最終的執行流程就如下圖所示:

wKiom1hHhbmxseDtAACidU9Y84s787.png

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM