Spring攔截器Interceptor與Servlet過濾器Filter詳解

一、先說結論

1.攔截器Interceptor與過濾器Filter的區別

《JAVA編程思想》截圖
在這裏插入圖片描述
兩者最大的區別在於:過濾器是在 Servlet 規範中定義的,是由 Servlet 容器支持的;攔截器是在 Spring 容器內的,由 Spring 框架支持。

因此,作爲 Spring 的一個組件,攔截器可以通過IOC容器進行管理,獲取其中的各個 bean 實例,對 spring 中的各種資源、對象,如 Service 對象、數據源、事務管理等進行調用;而過濾器則不能。

總的來說,兩者主要在如下方面存在着差異 :

  1. 過濾器是基於函數的回調,而攔截器是基於 Java 反射機制的
  2. 過濾器可以修改 request,而攔截器則不能(待證,Intercepter的preHandle可以向request添加attribute,不知道這個結論是否是個人的理解錯了)
  3. 過濾器需要在 servlet 容器中實現,攔截器可以適用於 JavaEE、JavaSE 等各種環境
  4. 攔截器可以調用 IOC 容器中的各種依賴,而過濾器不能
  5. 過濾器只能在請求的前後使用,而攔截器可以詳細到每個方法

2.攔截器鏈與過濾器鏈的執行順序

Filter 鏈執行順序

1.根據web.xml中配置的順序決定過濾器的先後關係

2.在doFilter方法中,位於FilterChain.doFilter方法之前的代碼先於Servlet執行

3.位於FilterChain.doFilter方法之後的代碼在Servlet執行之後執行
在這裏插入圖片描述

Interceptor 執行順序

1.根據springmvc.xml中配置的順序決定攔截器的先後關係
在這裏插入圖片描述

3.攔截器與過濾器的執行時機

過濾器先於攔截器執行
在這裏插入圖片描述
在這裏插入圖片描述

4.攔截器與過濾器的適用場景

攔截器Interceptor

  1. 日誌記錄 :機率請求信息的日誌,以便進行信息監控、信息統計等等
  2. 權限檢查 :對用戶的訪問權限,認證,或授權等進行檢查
  3. 性能監控 :通過攔截器在進入處理器前後分別記錄開始時間和結束時間,從而得到請求的處理時間
  4. 通用行爲 :讀取 cookie 得到用戶信息並將用戶對象放入請求頭中,從而方便後續流程使用

過濾器Filter

爲shiro權限過濾器,編碼過濾器,微信接口過濾器,上傳文件過濾器等。
在這裏插入圖片描述

二、攔截器與過濾器的使用及驗證

1.配置Filter

新建TestFilter類,實現過濾器中的邏輯

package com.framework;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 @author sunhongmin 2019-12-19
 Filter執行類,實現Filter接口,覆寫init、doFilter、destory方法
*/
public class TestFilter implements Filter{

	@Override
	public void destroy() {
		//Filter銷燬
   	//web容器調用destroy方法銷燬Filter。destroy方法在Filter的生命週期中僅執行一次。在destroy方法中,可以釋放過濾器使用的資源。
		System.out.println("Filter1 destroy");
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
			throws IOException, ServletException {
		//Filter 先攔截request, doFilter放行, response響應回來, Filter再次攔截 ,執行doFilter()後面的代碼
		//arg0.setAttribute("key0", "from Filter doFilter");
		System.out.println("Filter1 doFilter1");//doFilter之前的代碼,用於對請求進行處理
		arg2.doFilter(arg0, arg1);
		System.out.println("Filter1 doFilter2");//doFilter之後的代碼,用於對請求響應進行處理
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		//Filter初始化
		// web應用程序啓動時,web服務器將創建Filter的實例對象,並調用其init方法,完成對象的初始化功能,從而爲後續的用戶請求作好攔截的準備工作,filter對象只會創建一次,init方法也只會執行一次。通過init方法的參數,可獲得代表當前filter配置信息的FilterConfig對象。
		System.out.println("Filter1 init");
	}

}

在web.xml配置過濾器名稱,過濾路徑與class路徑,攔截所有請求

<!--配置過濾器-->  
  <filter>  
      <filter-name>FilterTest</filter-name>  
      <filter-class>com.framework.TestFilter</filter-class>  
  </filter>  
  <!--映射過濾器-->  
  <filter-mapping>  
      <filter-name>FilterTest</filter-name>  
      <!--“/*”表示攔截所有的請求 -->  
      <url-pattern>/*</url-pattern>  
  </filter-mapping>

2.配置Interceptor

新建TestInterceptor攔截器類,實現HandlerInterceptor接口

package com.framework;

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

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

public class TestIntercepter implements HandlerInterceptor{
	/**
	 * 	preHandle
		調用時間:Controller方法處理之前
		執行順序:鏈式Intercepter情況下,Intercepter按照聲明的順序一個接一個執行
		若返回false,則中斷執行,注意:不會進入afterCompletion
		處理參數,校驗權限,決定是否繼續執行
		
		postHandle
		調用前提:preHandle返回true
		調用時間:Controller方法處理完之後,DispatcherServlet進行視圖的渲染之前,也就是說在這個方法中你可以對ModelAndView進行操作
		執行順序:鏈式Intercepter情況下,Intercepter按照聲明的順序倒着執行。
		備註:postHandle雖然post打頭,但post、get方法都能處理
		一般可以通過它對 Controller 處理之後的 ModelAndView 對象進行操作。
		
		afterCompletion
		調用前提:preHandle返回true
		調用時間:DispatcherServlet進行視圖的渲染之後
		在整個請求處理完成(包括視圖渲染)後執行,主要用來進行一些資源的清理工作。
	 * */
	@Override
	public void afterCompletion(HttpServletRequest req, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		//System.out.println("key2:-->"+req.getAttribute("key2"));
		System.out.println(" Intercepter1 doAfterCompletion...");
	}

	@Override
	public void postHandle(HttpServletRequest req, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		//System.out.println("key2:-->"+req.getAttribute("key2"));
		System.out.println(" Intercepter1 doPostHandle...");
	}

	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		//arg0.setAttribute("key1", "from intercepter preHandle");
		System.out.println(" Intercepter1 doPreHandle...");
		return true;
	}

}

配置springmvc.xml文件

<mvc:interceptors>
        <mvc:interceptor>
            <!-- /** 表示攔截所有請求-->
            <mvc:mapping path="/**"/>
            <!-- 爲了排除干擾,將靜態資源請求排除,如果前後端分離可以忽略 -->
            <mvc:exclude-mapping path="/js/**" />
            <mvc:exclude-mapping path="/css/**" />
            <mvc:exclude-mapping path="/images/**" />
            <bean class="com.framework.TestIntercepter"/>
        </mvc:interceptor>
</mvc:interceptors>        

3.驗證執行順序

爲了查看攔截器與過濾器執行鏈的總體順序,加入TestFilter2與TestInterceptor2
web.xml
在這裏插入圖片描述
在這裏插入圖片描述
查看執行結果
在這裏插入圖片描述

總結

Filter只在Servlet前後起作用,而攔截器能夠深入到方法前後、異常拋出前後等,因此攔截器的使用具有更大的彈性。所以在spring結構的程序中,要優先使用攔截器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章