一婆翔、簡(jiǎn)述
過(guò)濾器(Filter),是在 Java Web 中將傳入的 request勺届、response 提前過(guò)濾掉一些信息、去除掉一些非法字符桑滩,或者提前設(shè)置一些參數(shù)、統(tǒng)一設(shè)置字符集等允睹。然后再傳入 Servlet 或 Struts2的action 進(jìn)行業(yè)務(wù)邏輯處理运准。比如過(guò)濾掉非法 url(不是 login.do 的地址請(qǐng)求,如果用戶沒(méi)有登錄都過(guò)濾掉)缭受。
攔截器(Interceptor)胁澳,是面向切面編程(AOP,Aspect Oriented Program)的米者。就是在 Service 或者一個(gè)方法前調(diào)用一個(gè)方法韭畸,或者在方法后調(diào)用一個(gè)方法。比如動(dòng)態(tài)代理就是攔截器的簡(jiǎn)單實(shí)現(xiàn)蔓搞,在調(diào)用方法前打印出字符串(或者做其它業(yè)務(wù)邏輯的操作)胰丁,也可以在調(diào)用方法后打印出字符串,甚至在拋出異常的時(shí)候做業(yè)務(wù)邏輯的操作败明。
二隘马、過(guò)濾器(Filter)與攔截器(Interceptor)對(duì)比
1??通俗理解
- 過(guò)濾器(Filter):有一堆東西的時(shí)候太防,只選擇符合要求的東西妻顶。定義這些要求的工具,就是過(guò)濾器蜒车。(理解:一堆字母中取一個(gè)B)
- 攔截器(Interceptor):在一個(gè)流程正在進(jìn)行的時(shí)候讳嘱,干預(yù)它的進(jìn)展,甚至終止它進(jìn)行酿愧,這是攔截器做的事情沥潭。(理解:一堆字母中,干預(yù)它嬉挡,通過(guò)驗(yàn)證的少點(diǎn)钝鸽,順便干點(diǎn)別的東西)
2??主要區(qū)別
- 過(guò)濾器是基于函數(shù)回調(diào)。而攔截器是基于 Java 的反射機(jī)制的庞钢。
- 過(guò)濾器依賴于 servlet 容器拔恰。攔截器不依賴于 servlet 容器。
- 過(guò)濾器幾乎可以對(duì)所有的請(qǐng)求起作用基括。攔截器只能對(duì) action 請(qǐng)求起作用颜懊。
- 過(guò)濾器不能訪問(wèn) action 上下文、值棧里的對(duì)象。而攔截器可以訪問(wèn)河爹。
- 在 action 的生命周期中匠璧,過(guò)濾器只能在容器初始化時(shí)被調(diào)用一次。攔截器可以多次被調(diào)用咸这。
- 攔截器可以獲取 IOC 容器中的各個(gè) bean (基于 FactoryBean 接口)夷恍,在攔截器里注入一個(gè)service,可以調(diào)用業(yè)務(wù)邏輯媳维。而過(guò)濾器就不行裁厅。
3??本質(zhì)區(qū)別
從靈活性上說(shuō)攔截器(Interceptor)功能更強(qiáng)大些,過(guò)濾器(Filter)能做的事情它都能做侨艾,而且可以在請(qǐng)求前执虹,請(qǐng)求后執(zhí)行,比較靈活唠梨。過(guò)濾器(Filter)主要是針對(duì) URL 地址做一個(gè)編碼的事情袋励、過(guò)濾掉沒(méi)用的參數(shù)、安全校驗(yàn)(比較泛的当叭,比如登錄不登錄之類)茬故,太細(xì)的活兒還是建議用攔截器(Interceptor)。
4??執(zhí)行順序過(guò)濾前---攔截前---Action處理---攔截后---過(guò)濾后
過(guò)濾器(Filter)是在請(qǐng)求進(jìn)入容器后己英,但還未進(jìn)入 Servlet 之前進(jìn)行預(yù)處理的间螟。請(qǐng)求結(jié)束返回也是,是在 Servlet 處理完后损肛,返回給前端之前厢破。所以過(guò)濾器(Filter)的doFilter(ServletRequest request, ServletResponse response, FilterChain chain )
的入?yún)⑹?ServletRequest,而不是 httpservletrequest治拿。因?yàn)檫^(guò)濾器是在 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??過(guò)濾器(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
方法來(lái)初始化過(guò)濾器(Filter)加勤。在 web 應(yīng)用程序被移除或者是服務(wù)器關(guān)閉時(shí)仙辟,會(huì)調(diào)用public void destroy()
來(lái)銷毀過(guò)濾器(Filter)。
在一個(gè)應(yīng)用程序中一個(gè)過(guò)濾器(Filter)只會(huì)被創(chuàng)建和銷毀一次鳄梅。在初始化之后叠国,過(guò)濾器(Filter)中聲明了public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
方法,用來(lái)實(shí)現(xiàn)一些需要在攔截完成之后的業(yè)務(wù)邏輯戴尸。
注意到上面的 doFilter() 的參數(shù)中粟焊,有 FilterChain chain 這個(gè)參數(shù),它是傳遞過(guò)來(lái)的攔截鏈對(duì)象孙蒙,里面包含了用戶定義的一系列的攔截器项棠,這些攔截器根據(jù)其在 web.xml 中定義的順序依次被執(zhí)行。當(dāng)用戶的信息驗(yàn)證通過(guò)或者當(dāng)前攔截器不起作用時(shí)挎峦,可以執(zhí)行chain.doFilter(request, response);
來(lái)跳過(guò)當(dāng)前攔截器來(lái)執(zhí)行攔截器鏈中的下一個(gè)攔截器香追,該方法的調(diào)用作為分水嶺。事實(shí)上調(diào)用 Servlet 的 doService() 是在chain.doFilter(request, response);
這個(gè)方法中進(jìn)行的坦胶。
三透典、過(guò)濾器(Filter)與攔截器(Interceptor)的應(yīng)用場(chǎng)景
SpringMVC 的攔截器類似于 Servlet 開發(fā)中的過(guò)濾器(Filter),用于對(duì)處理器進(jìn)行預(yù)處理和后處理顿苇。
1??【日志記錄】記錄請(qǐng)求信息的日志峭咒,以便進(jìn)行信息監(jiān)控、信息統(tǒng)計(jì)纪岁、計(jì)算 PV(Page View) 等凑队。
2??【權(quán)限檢查】如登錄檢測(cè),進(jìn)入處理器檢測(cè)是否登錄幔翰,如果沒(méi)有直接返回到登錄頁(yè)面漩氨。
3??【性能監(jiān)控】有時(shí)候系統(tǒng)莫名其妙的慢,可以通過(guò)攔截器(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ǔ)充說(shuō)明
Spring 的攔截器(Interceptor)與 Servlet 的過(guò)濾器(Filter)有相似之處尚粘,都能實(shí)現(xiàn)權(quán)限檢查择卦、日志記錄等郎嫁。不同的是:
- 使用范圍不同:過(guò)濾器(Filter)是 Servlet 規(guī)范規(guī)定的秉继,只能用于 web 程序中泽铛。而攔截器既可以用于 web 程序,也可以用于 Application盔腔、Swing 程序中。
- 規(guī)范不同:過(guò)濾器(Filter)是在 Servlet 規(guī)范中定義的澈蝙,是 Servlet 容器支持的。而攔截器(Interceptor)是在 Spring 容器內(nèi)的灯荧,是 Spring 框架支持的盐杂。
- 使用的資源不同:同其他的代碼塊一樣,攔截器也是一個(gè) Spring 的組件链烈,歸 Spring 管理,配置在 Spring 文件中强衡,因此能使用 Spring 里的任何資源、對(duì)象感挥,例如Service對(duì)象、數(shù)據(jù)源触幼、事務(wù)管理等究飞,通過(guò) IOC 注入到攔截器即可堂鲤。而過(guò)濾器(Filter)則不能媒峡。
- 深度不同:過(guò)濾器(Filter)在只在 Servlet 前后起作用瘟栖。而攔截器(Interceptor)能夠深入到方法前后丝蹭、異常拋出前后等,因此攔截器的使用具有更大的彈性镜沽。所以在 Spring 構(gòu)架的程序中贱田,要優(yōu)先使用攔截器缅茉。
實(shí)際上過(guò)濾器(Filter)與攔截器(Interceptor)極其相似男摧,區(qū)別只是過(guò)濾器(Filter)不能直接對(duì)用戶生成響應(yīng)蔬墩。實(shí)際上過(guò)濾器(Filter)里 doFilter() 里的代碼就是從多個(gè) Servlet 的 service() 里抽取的通用代碼耗拓,通過(guò)使用過(guò)濾器(Filter)可以實(shí)現(xiàn)更好的復(fù)用。
過(guò)濾器(Filter)是一個(gè)可以復(fù)用的代碼片段乔询,可以用來(lái)轉(zhuǎn)換 Http 請(qǐng)求、響應(yīng)和頭信息黄锤。過(guò)濾器(Filter)不像 Servlet食拜,它不能產(chǎn)生一個(gè)請(qǐng)求或者響應(yīng)鸵熟,它只是修改對(duì)某一資源的請(qǐng)求负甸,或者修改從某一資源的響應(yīng)。
JSR 中說(shuō)明的是煮盼,按照多個(gè)匹配的過(guò)濾器(Filter)带污,是按照其在 web.xml 中配置的順序來(lái)執(zhí)行的。所以這也就是鱼冀,把自己的過(guò)濾器(Filter)或者其他的過(guò)濾器(Filter)(比如 UrlRewrite 的過(guò)濾器(Filter))放在 Struts2 的 DispatcherFilter 的前面的原因报破。因?yàn)樗鼈冃枰谡?qǐng)求被 Struts2 框架處理之前千绪,做一些前置的工作。
當(dāng)過(guò)濾器(Filter)被調(diào)用盹靴,并且進(jìn)入了 Struts2 的 DispatcherFilter 中后,Struts2 會(huì)按照在 Action 中配置的 Interceptor Stack 中的 Interceptor 的順序稿静,來(lái)調(diào)用Interceptor辕狰。