文章目錄
一、先說結論
1.攔截器Interceptor與過濾器Filter的區別
《JAVA編程思想》截圖
兩者最大的區別在於:過濾器是在 Servlet 規範中定義的,是由 Servlet 容器支持的;攔截器是在 Spring 容器內的,由 Spring 框架支持。
因此,作爲 Spring 的一個組件,攔截器可以通過IOC容器進行管理,獲取其中的各個 bean 實例,對 spring 中的各種資源、對象,如 Service 對象、數據源、事務管理等進行調用;而過濾器則不能。
總的來說,兩者主要在如下方面存在着差異 :
- 過濾器是基於函數的回調,而攔截器是基於 Java 反射機制的
- 過濾器可以修改 request,而攔截器則不能(待證,Intercepter的preHandle可以向request添加attribute,不知道這個結論是否是個人的理解錯了)
- 過濾器需要在 servlet 容器中實現,攔截器可以適用於 JavaEE、JavaSE 等各種環境
- 攔截器可以調用 IOC 容器中的各種依賴,而過濾器不能
- 過濾器只能在請求的前後使用,而攔截器可以詳細到每個方法
2.攔截器鏈與過濾器鏈的執行順序
Filter 鏈執行順序
1.根據web.xml中配置的順序決定過濾器的先後關係
2.在doFilter方法中,位於FilterChain.doFilter方法之前的代碼先於Servlet執行
3.位於FilterChain.doFilter方法之後的代碼在Servlet執行之後執行
Interceptor 執行順序
1.根據springmvc.xml中配置的順序決定攔截器的先後關係
3.攔截器與過濾器的執行時機
過濾器先於攔截器執行
4.攔截器與過濾器的適用場景
攔截器Interceptor
- 日誌記錄 :機率請求信息的日誌,以便進行信息監控、信息統計等等
- 權限檢查 :對用戶的訪問權限,認證,或授權等進行檢查
- 性能監控 :通過攔截器在進入處理器前後分別記錄開始時間和結束時間,從而得到請求的處理時間
- 通用行爲 :讀取 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結構的程序中,要優先使用攔截器。