一、簡述
過濾器(Filter),是在 Java Web 中將傳入的 request、response 提前過濾掉一些信息、去除掉一些非法字符,或者提前設(shè)置一些參數(shù)、統(tǒng)一設(shè)置字符集等。然后再傳入 Servlet 或 Struts2的action 進(jìn)行業(yè)務(wù)邏輯處理。比如過濾掉非法 url(不是 login.do 的地址請(qǐng)求,如果用戶沒有登錄都過濾掉)。
攔截器(Interceptor),是面向切面編程(AOP,Aspect Oriented Program)的。就是在 Service 或者一個(gè)方法前調(diào)用一個(gè)方法,或者在方法后調(diào)用一個(gè)方法。比如動(dòng)態(tài)代理就是攔截器的簡單實(shí)現(xiàn),在調(diào)用方法前打印出字符串(或者做其它業(yè)務(wù)邏輯的操作),也可以在調(diào)用方法后打印出字符串,甚至在拋出異常的時(shí)候做業(yè)務(wù)邏輯的操作。
二、過濾器(Filter)與攔截器(Interceptor)對(duì)比
1??通俗理解
- 過濾器(Filter):有一堆東西的時(shí)候,只選擇符合要求的東西。定義這些要求的工具,就是過濾器。(理解:一堆字母中取一個(gè)B)
- 攔截器(Interceptor):在一個(gè)流程正在進(jìn)行的時(shí)候,干預(yù)它的進(jìn)展,甚至終止它進(jìn)行,這是攔截器做的事情。(理解:一堆字母中,干預(yù)它,通過驗(yàn)證的少點(diǎn),順便干點(diǎn)別的東西)
2??主要區(qū)別
- 過濾器是基于函數(shù)回調(diào)。而攔截器是基于 Java 的反射機(jī)制的。
- 過濾器依賴于 servlet 容器。攔截器不依賴于 servlet 容器。
- 過濾器幾乎可以對(duì)所有的請(qǐng)求起作用。攔截器只能對(duì) action 請(qǐng)求起作用。
- 過濾器不能訪問 action 上下文、值棧里的對(duì)象。而攔截器可以訪問。
- 在 action 的生命周期中,過濾器只能在容器初始化時(shí)被調(diào)用一次。攔截器可以多次被調(diào)用。
- 攔截器可以獲取 IOC 容器中的各個(gè) bean (基于 FactoryBean 接口),在攔截器里注入一個(gè)service,可以調(diào)用業(yè)務(wù)邏輯。而過濾器就不行。
3??本質(zhì)區(qū)別
從靈活性上說攔截器(Interceptor)功能更強(qiáng)大些,過濾器(Filter)能做的事情它都能做,而且可以在請(qǐng)求前,請(qǐng)求后執(zhí)行,比較靈活。過濾器(Filter)主要是針對(duì) URL 地址做一個(gè)編碼的事情、過濾掉沒用的參數(shù)、安全校驗(yàn)(比較泛的,比如登錄不登錄之類),太細(xì)的活兒還是建議用攔截器(Interceptor)。
4??執(zhí)行順序過濾前---攔截前---Action處理---攔截后---過濾后
過濾器(Filter)是在請(qǐng)求進(jìn)入容器后,但還未進(jìn)入 Servlet 之前進(jìn)行預(yù)處理的。請(qǐng)求結(jié)束返回也是,是在 Servlet 處理完后,返回給前端之前。所以過濾器(Filter)的doFilter(ServletRequest request, ServletResponse response, FilterChain chain )
的入?yún)⑹?ServletRequest,而不是 httpservletrequest。因?yàn)檫^濾器是在 httpservlet之前。
@Override
public void init(FilterConfig arg0) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("before...");
chain.doFilter(request, response);
System.out.println("after...");
}
@Override
public void destroy() {}
5??過濾器(Filter)跟Servlet一樣都是由服務(wù)器負(fù)責(zé)創(chuàng)建和銷毀的。
在 web 應(yīng)用程序啟動(dòng)時(shí),服務(wù)器會(huì)根據(jù)應(yīng)用程序的 web.xml 的配置信息調(diào)用public void init(FilterConfig filterConfig) throws ServletException
方法來初始化過濾器(Filter)。在 web 應(yīng)用程序被移除或者是服務(wù)器關(guān)閉時(shí),會(huì)調(diào)用public void destroy()
來銷毀過濾器(Filter)。
在一個(gè)應(yīng)用程序中一個(gè)過濾器(Filter)只會(huì)被創(chuàng)建和銷毀一次。在初始化之后,過濾器(Filter)中聲明了public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
方法,用來實(shí)現(xiàn)一些需要在攔截完成之后的業(yè)務(wù)邏輯。
注意到上面的 doFilter() 的參數(shù)中,有 FilterChain chain 這個(gè)參數(shù),它是傳遞過來的攔截鏈對(duì)象,里面包含了用戶定義的一系列的攔截器,這些攔截器根據(jù)其在 web.xml 中定義的順序依次被執(zhí)行。當(dāng)用戶的信息驗(yàn)證通過或者當(dāng)前攔截器不起作用時(shí),可以執(zhí)行chain.doFilter(request, response);
來跳過當(dāng)前攔截器來執(zhí)行攔截器鏈中的下一個(gè)攔截器,該方法的調(diào)用作為分水嶺。事實(shí)上調(diào)用 Servlet 的 doService() 是在chain.doFilter(request, response);
這個(gè)方法中進(jìn)行的。
三、過濾器(Filter)與攔截器(Interceptor)的應(yīng)用場景
SpringMVC 的攔截器類似于 Servlet 開發(fā)中的過濾器(Filter),用于對(duì)處理器進(jìn)行預(yù)處理和后處理。
1??【日志記錄】記錄請(qǐng)求信息的日志,以便進(jìn)行信息監(jiān)控、信息統(tǒng)計(jì)、計(jì)算 PV(Page View) 等。
2??【權(quán)限檢查】如登錄檢測,進(jìn)入處理器檢測是否登錄,如果沒有直接返回到登錄頁面。
3??【性能監(jiān)控】有時(shí)候系統(tǒng)莫名其妙的慢,可以通過攔截器(Interceptor)在進(jìn)入處理器之前記錄開始時(shí)間,在處理完后記錄結(jié)束時(shí)間,從而得到該請(qǐng)求的處理時(shí)間(如果有反向代理,如 apache 可以自動(dòng)記錄)。
4??【通用行為】讀取 cookie 得到用戶信息并將用戶對(duì)象放入請(qǐng)求,從而方便后續(xù)流程使用。還有如提取 Locale、Theme 信息等,只要是多個(gè)處理器都需要的即可使用攔截器(Interceptor)實(shí)現(xiàn)。
5??【OpenSessionInView】如 hibernate,在進(jìn)入處理器打開 Session,在完成后關(guān)閉 Session。
四、補(bǔ)充說明
Spring 的攔截器(Interceptor)與 Servlet 的過濾器(Filter)有相似之處,都能實(shí)現(xiàn)權(quán)限檢查、日志記錄等。不同的是:
- 使用范圍不同:過濾器(Filter)是 Servlet 規(guī)范規(guī)定的,只能用于 web 程序中。而攔截器既可以用于 web 程序,也可以用于 Application、Swing 程序中。
- 規(guī)范不同:過濾器(Filter)是在 Servlet 規(guī)范中定義的,是 Servlet 容器支持的。而攔截器(Interceptor)是在 Spring 容器內(nèi)的,是 Spring 框架支持的。
- 使用的資源不同:同其他的代碼塊一樣,攔截器也是一個(gè) Spring 的組件,歸 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何資源、對(duì)象,例如Service對(duì)象、數(shù)據(jù)源、事務(wù)管理等,通過 IOC 注入到攔截器即可。而過濾器(Filter)則不能。
- 深度不同:過濾器(Filter)在只在 Servlet 前后起作用。而攔截器(Interceptor)能夠深入到方法前后、異常拋出前后等,因此攔截器的使用具有更大的彈性。所以在 Spring 構(gòu)架的程序中,要優(yōu)先使用攔截器。
實(shí)際上過濾器(Filter)與攔截器(Interceptor)極其相似,區(qū)別只是過濾器(Filter)不能直接對(duì)用戶生成響應(yīng)。實(shí)際上過濾器(Filter)里 doFilter() 里的代碼就是從多個(gè) Servlet 的 service() 里抽取的通用代碼,通過使用過濾器(Filter)可以實(shí)現(xiàn)更好的復(fù)用。
過濾器(Filter)是一個(gè)可以復(fù)用的代碼片段,可以用來轉(zhuǎn)換 Http 請(qǐng)求、響應(yīng)和頭信息。過濾器(Filter)不像 Servlet,它不能產(chǎn)生一個(gè)請(qǐng)求或者響應(yīng),它只是修改對(duì)某一資源的請(qǐng)求,或者修改從某一資源的響應(yīng)。
JSR 中說明的是,按照多個(gè)匹配的過濾器(Filter),是按照其在 web.xml 中配置的順序來執(zhí)行的。所以這也就是,把自己的過濾器(Filter)或者其他的過濾器(Filter)(比如 UrlRewrite 的過濾器(Filter))放在 Struts2 的 DispatcherFilter 的前面的原因。因?yàn)樗鼈冃枰谡?qǐng)求被 Struts2 框架處理之前,做一些前置的工作。
當(dāng)過濾器(Filter)被調(diào)用,并且進(jìn)入了 Struts2 的 DispatcherFilter 中后,Struts2 會(huì)按照在 Action 中配置的 Interceptor Stack 中的 Interceptor 的順序,來調(diào)用Interceptor。