解讀過濾器(Filter)與攔截器(Interceptor)的區(qū)別及說明
1 過濾器(Filter)
Servlet 中的過濾器 Filter 實(shí)現(xiàn)了 javax.servlet.Filter 接口的服務(wù)器端程序,主要用途是設(shè)置字符集(CharacterEncodingFilter)、控制權(quán)限、控制轉(zhuǎn)向、用戶是否已經(jīng)登陸、有沒有權(quán)限訪問該頁面等。
其工作原理是,只要你在 web.xml 文件配置好要攔截的客戶端請(qǐng)求,它都會(huì)幫你攔截到請(qǐng)求。此時(shí),其實(shí)你可以對(duì)請(qǐng)求或響應(yīng)(request、response)統(tǒng)一設(shè)置編碼;它隨 web 應(yīng)用啟動(dòng)而啟動(dòng),只初始化一次,以后就可以攔截相關(guān)請(qǐng)求。只有當(dāng)你的 web 應(yīng)用停止或重新部署的時(shí)候才銷毀。
- Filter 可以認(rèn)為是 Servlet 的一種”加強(qiáng)版”。它主要用于對(duì)用戶請(qǐng)求進(jìn)行預(yù)處理,也可以對(duì)HttpServletResponse 進(jìn)行后處理,是個(gè)典型的處理鏈;
- Filter 可以對(duì)用戶請(qǐng)求生成響應(yīng),和 Servlet 相同;
- 處理流程:用戶請(qǐng)求 -> Filter 預(yù)處理 -> Servlet 處理請(qǐng)求生成響應(yīng) -> Filter 對(duì)響應(yīng)進(jìn)行后處理。
1.1 Filter 的用處
- 在 HttpServletRequest 到達(dá) Servlet 之前,攔截客戶的 HttpServletRequest;
- 根據(jù)需要檢查 HttpServletRequest,也可以修改 HttpServletRequest 頭和數(shù)據(jù);
- 在 HttpServletResponse 到達(dá)客戶端之前,攔截 HttpServletResponse;
- 根據(jù)需要檢查 HttpServletResponse,也可以修改 HttpServletResponse頭和數(shù)據(jù)。
Filter 有如下幾個(gè)種類。
1.2 Filter 的種類
- 用戶授權(quán)的 Filter:Filter 負(fù)責(zé)檢查用戶請(qǐng)求,根據(jù)請(qǐng)求過濾用戶非法請(qǐng)求;
- 日志 Filter:詳細(xì)記錄某些特殊的用戶請(qǐng)求;
- 負(fù)責(zé)解碼的 Filter:包括對(duì)非標(biāo)準(zhǔn)編碼的請(qǐng)求解碼;
- 能改變 XML 內(nèi)容的 XSLT Filter 等。
Filter 可以負(fù)責(zé)攔截多個(gè)請(qǐng)求或響應(yīng),一個(gè)請(qǐng)求或響應(yīng)也可以被多個(gè) Filter 攔截。
1.3 創(chuàng)建 Filter 的步驟
- 創(chuàng)建 Filter 處理類,并實(shí)現(xiàn) javax.servlet.Filter 接口;
- web.xml 文件中配置 Filter(或者使用 @WebFilter 注解)。
javax.servlet.Filter 接口中定義了三個(gè)方法:
- void init(FilterConfig config) 用于完成 Filter 的初始化;
- void destory() 用于 Filter 銷毀前,完成某些資源的回收;
- void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) 實(shí)現(xiàn)過濾功能。
doFilter 方法就是對(duì)每個(gè)請(qǐng)求及響應(yīng)增加的額外處理。該方法可以實(shí)現(xiàn)對(duì)用戶請(qǐng)求進(jìn)行預(yù)處理(ServletRequest request),也可實(shí)現(xiàn)對(duì)服務(wù)器響應(yīng)進(jìn)行后處理(ServletResponse response)。
它們的分界線為,是否調(diào)用了 chain.doFilter()。執(zhí)行該方法之前,即對(duì)用戶請(qǐng)求進(jìn)行預(yù)處理;執(zhí)行該方法之后,即對(duì)服務(wù)器響應(yīng)進(jìn)行后處理。
2 攔截器(Interceptor)
攔截器是在面向切面編程中應(yīng)用的,就是 service 或一個(gè)方法前/后調(diào)用一個(gè)方法。是基礎(chǔ) Java 的反射機(jī)制。
攔截不是在 web.xml,而是在 AOP(Aspect-Oriented Programming)中用于某個(gè)方法或字段被訪問之前,進(jìn)行攔截。然后在之前或之后加入某些操作,甚至在拋出異常的時(shí)候做業(yè)務(wù)邏輯的操作。攔擊器是 AOP 的一種實(shí)現(xiàn)策略。
2.1 攔截器的實(shí)現(xiàn)方式
SpringMVC 中的 Interceptor 攔截請(qǐng)求是通過 HandlerInterceptor 來實(shí)現(xiàn)的。在 SpringMVC 中定義 Interceptor 主要有兩種方式:
- 實(shí)現(xiàn) Spring 的 HandlerInterceptor 接口或者繼承了實(shí)現(xiàn) HandlerInterceptor 接口的類(比如HandlerInterceptorAdapter);
- 實(shí)現(xiàn) Spring 的 WebRequestInterceptor 接口,或者繼承了實(shí)現(xiàn)WebRequestInterceptor接口的類。
Interceptor 中的方法:
preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法
顧名思義,該方法將在請(qǐng)求處理之前進(jìn)行調(diào)用。
SpringMVC 中的 Interceptor 是鏈?zhǔn)秸{(diào)用。在一個(gè)應(yīng)用中或者說是在一個(gè)請(qǐng)求中可以同時(shí)存在多個(gè) Interceptor,每個(gè) Interceptor 的調(diào)用會(huì)依據(jù)它的聲明順序依次執(zhí)行,而且最先執(zhí)行的都是 Interceptor 中的 preHandle 方法。
所以可以在這個(gè)方法中進(jìn)行一些前置初始化操作或者是對(duì)當(dāng)前請(qǐng)求的一個(gè)預(yù)處理,也可以在這個(gè)方法中進(jìn)行一些判斷來決定請(qǐng)求是否要繼續(xù)進(jìn)行下去。該方法的返回值是布爾值 Boolean 類型的,當(dāng)它返回為 false 時(shí),表示請(qǐng)求結(jié)束,后續(xù)的 Interceptor 和Controller 都不會(huì)再執(zhí)行;當(dāng)返回值為 true 時(shí)就會(huì)繼續(xù)調(diào)用下一個(gè) Interceptor 的 preHandle 方法。如果已經(jīng)是最后一個(gè) Interceptor 的時(shí)候就會(huì)是調(diào)用當(dāng)前請(qǐng)求的Controller 方法。
postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法
由 preHandle 方法的解釋我們知道,這個(gè)方法包括后面要說到的 afterCompletion 方法都只能是在當(dāng)前所屬的 Interceptor 的 preHandle 方法的返回值為 true 時(shí)才能被調(diào)用。
postHandle 方法,顧名思義就是在當(dāng)前請(qǐng)求進(jìn)行處理之后,也就是 Controller 方法調(diào)用之后執(zhí)行,但是它會(huì)在 DispatcherServlet 進(jìn)行視圖返回渲染之前被調(diào)用。所以,我們可以在這個(gè)方法中對(duì) Controller 處理之后的 ModelAndView 對(duì)象進(jìn)行操作。
postHandle 方法被調(diào)用的方向跟 preHandle 是相反的。也就是說,先聲明的 Interceptor 的 postHandle 方法反而會(huì)后執(zhí)行。這和 Struts2 里面的 Interceptor 的執(zhí)行過程有點(diǎn)類似。Struts2 里面的 Interceptor 的執(zhí)行過程也是鏈?zhǔn)降模皇窃?Struts2 里面需要手動(dòng)調(diào)用 ActionInvocation 的 invoke 方法來觸發(fā)對(duì)下一個(gè) Interceptor 或者是 Action 的調(diào)用,然后每一個(gè) Interceptor 中在 invoke 方法調(diào)用之前的內(nèi)容都是按照聲明順序執(zhí)行的,而 invoke 方法之后的內(nèi)容就是反向的。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法
該方法也是需要當(dāng)前對(duì)應(yīng)的 Interceptor 的p reHandle 方法的返回值為 true 時(shí)才會(huì)執(zhí)行。顧名思義,該方法將在整個(gè)請(qǐng)求結(jié)束之后,也就是在 DispatcherServlet 渲染了對(duì)應(yīng)的視圖之后執(zhí)行。這個(gè)方法的主要作用是用于進(jìn)行資源清理工作的。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{
private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
//after the handler is executed
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
long startTime = (Long)request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
//統(tǒng)計(jì)耗時(shí)
long executeTime = endTime - startTime;
//modified the exisitng modelAndView
modelAndView.addObject("executeTime",executeTime);
//log it
if(logger.isDebugEnabled()){
logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
}
}
}
2.1.1 非 SpringBoot 項(xiàng)目實(shí)現(xiàn)方式
使用 mvc:interceptors 標(biāo)簽來聲明需要加入到 SpringMVC 攔截器鏈中的攔截器。
<mvc:interceptors>
<!-- 使用bean定義一個(gè)Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請(qǐng)求 -->
<bean class="com.company.app.web.interceptor.AllInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/parent/**"/>
<bean class="com.company.authorization.interceptor.SecurityInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/parent/**"/>
<bean class="com.company.authorization.interceptor.SecuritySystemInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
可以利用 mvc:interceptors 標(biāo)簽聲明一系列的攔截器,然后它們就可以形成一個(gè)攔截器鏈。攔截器的執(zhí)行順序是按聲明的先后順序執(zhí)行的,先聲明的攔截器中的 preHandle 方法會(huì)先執(zhí)行,然而它的 postHandle 方法和 afterCompletion 方法卻會(huì)后執(zhí)行。
在 mvc:interceptors 標(biāo)簽下聲明 interceptor 主要有兩種方式:
- 直接定義一個(gè) Interceptor 實(shí)現(xiàn)類的 bean 對(duì)象。使用這種方式聲明的 Interceptor攔截器將會(huì)對(duì)所有的請(qǐng)求進(jìn)行攔截;
- 使用 mvc:interceptor 標(biāo)簽進(jìn)行聲明。使用這種方式進(jìn)行聲明的 Interceptor 可以通過 mvc:mapping子標(biāo)簽來定義需要進(jìn)行攔截的請(qǐng)求路徑。
經(jīng)過上述兩步之后,定義的攔截器就會(huì)發(fā)生作用對(duì)特定的請(qǐng)求進(jìn)行攔截了。
2.1.2 Spring Boot 項(xiàng)目
配置攔截器
@Configuration
//@Configurationpublic
public class WebAppConfigurer implements WebMvcConfigurer {
/**
* 配置攔截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 多個(gè)攔截器組成一個(gè)攔截器鏈
registry.addInterceptor(new ExecuteTimeInterceptor()).addPathPatterns("/**");
//API限流攔截
registry.addInterceptor(accessLimitAjaxInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");
registry.addInterceptor(accessInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");
}
}
2.2 攔截器(interceptor)使用
- 請(qǐng)求到達(dá) DispatcherServlet;
- DispatcherServlet 發(fā)送至 Interceptor,執(zhí)行 preHandler;
- 請(qǐng)求到達(dá) Controller;
- 請(qǐng)求結(jié)束后,執(zhí)行 postHandler。
3 過濾器(Filter)與 攔截器(Interceptor)的區(qū)別
Spring 的 Interceptor(攔截器)與 Servlet 的 Filter 有相似之處,比如二者都是 AOP 編程思想的體現(xiàn),都能實(shí)現(xiàn)權(quán)限檢查、日志記錄等。
不同之處總結(jié)如下:

3.1 執(zhí)行順序
用戶請(qǐng)求 -> 過濾前 -> 攔截前 -> Action處理 -> 攔截后 -> 過濾后 -> 響應(yīng)

4 過濾器(Filter)與 攔截器(Interceptor)常見用途
- Authentication Filters
- Logging and Auditing Filtersx
- Image conversion Filters
- Data compression Filters
- Encryption Filters
- Tokenizing Filters
- Filters that trigger resource access events
- XSL/T filters
- Mime-type chain Filter
Request Filters 可以實(shí)現(xiàn):
- 執(zhí)行安全檢查
- 格式化請(qǐng)求頭和主體
- 審查或者記錄日志
- 根據(jù)請(qǐng)求內(nèi)容授權(quán)或者限制用戶訪問
- 根據(jù)請(qǐng)求頻率限制用戶訪問
Response Filters 可以實(shí)現(xiàn):
- 壓縮響應(yīng)內(nèi)容:比如讓下載的內(nèi)容更小
- 追加或者修改響應(yīng)
- 創(chuàng)建或者整體修改響應(yīng)
- 根據(jù)地方不同修改響應(yīng)內(nèi)容
5 總結(jié)
5.1 過濾器
所謂過濾器顧名思義是用來過濾的。在 Java Web中,傳入的 request、response 提前過濾掉一些信息,或者提前設(shè)置一些參數(shù);然后再傳入 Servlet 或者 Struts 的 action 進(jìn)行業(yè)務(wù)邏輯。比如,過濾掉非法 URL(不是 login.do 的地址請(qǐng)求,如果用戶沒有登陸都過濾掉),或者在傳入 Servlet 或者 Struts 的 action 前統(tǒng)一設(shè)置字符集,或者去除掉一些非法字符(聊天室經(jīng)常用到的,一些罵人的話)。
Filter 流程是線性的, URL 傳來之后,檢查之后,可保持原來的流程繼續(xù)向下執(zhí)行,被下一個(gè) filter、servlet 接收等。
5.2 Java 的攔截器
主要是用在插件上,擴(kuò)展件上比如 Hibernate Spring Struts2等 有點(diǎn)類似面向切片的技術(shù),在用之前先要在配置文件即 XML 文件里進(jìn)行對(duì)應(yīng)的聲明。
攔截器(Interceptor)是基于 Java 的反射機(jī)制,而過濾器(Filter)是基于函數(shù)回調(diào)。從靈活性上說攔截器功能更強(qiáng)大些,F(xiàn)ilter 能做的事情,攔截器都能做,而且可以在請(qǐng)求前、請(qǐng)求后執(zhí)行,比較靈活。Filter 主要是針對(duì) URL 地址做一個(gè)編碼的事情、過濾掉沒用的參數(shù)、安全校驗(yàn)(比如登錄不登錄之類),太細(xì)的話,還是建議用攔截器。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 解讀過濾器(Filter)和攔截器(Interceptor)的使用
- Spring Boot攔截器Interceptor與過濾器Filter深度解析(區(qū)別、實(shí)現(xiàn)與實(shí)戰(zhàn)指南)
- Spring?Boot攔截器Interceptor與過濾器Filter實(shí)戰(zhàn)指南
- Spring Boot攔截器Interceptor與過濾器Filter詳細(xì)教程(示例詳解)
- Spring中過濾器(Filter)和攔截器(Interceptor)的區(qū)別和聯(lián)系解析
- Java中過濾器 (Filter) 和 攔截器 (Interceptor)的使用
- 淺談SpringMVC的攔截器(Interceptor)和Servlet 的過濾器(Filter)的區(qū)別與聯(lián)系 及SpringMVC 的配置文件
相關(guān)文章
java數(shù)據(jù)庫連接池和數(shù)據(jù)庫連接示例
這篇文章主要介紹了java數(shù)據(jù)庫連接池和數(shù)據(jù)庫連接示例,需要的朋友可以參考下2014-05-05
maven項(xiàng)目錯(cuò)誤:找不到或無法加載主類?XXX問題
這篇文章主要介紹了maven項(xiàng)目錯(cuò)誤:找不到或無法加載主類?XXX問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02

