前言:在工做中,遇到須要記錄日誌的狀況,不知道該選擇過濾器仍是攔截器,故總結了一下。css
java過濾器可以對目標資源的請求和響應進行截取。過濾器的工做方式分爲四種html
能夠經過 doFilter 方法的 request、response 提早過濾一些不想要的信息,統一設置一些參數、統一設置字符集、控制權限是否登陸等。java
<!-- 定義Filter --> <filter> <!-- Filter的名字 --> <filter-name>loginFilter</filter-name> <!-- Filter的實現類 --> <filter-class>com.yule.common.filters.LoginFilter</filter-class> </filter> <!-- 定義Filter攔截的URL地址 --> <filter-mapping> <!-- Filter的名字 --> <filter-name>loginFilter</filter-name> <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求--> <url-pattern>/*</url-pattern> </filter-mapping>
<filter-mapping> <filter-name>myFilter</filter-name> <servlet-name>目標資源</servlet-name> <dispatcher>REQUEST</dispatcher> </filter-mapping>
四中工做方式經過配置 <dispatcher> 標籤來決定web
<filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/error.jsp</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping> <error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page>
根據 web.xml 的代碼順序來決定過濾器的執行順序。Filter 鏈: 一個Web應用中,能夠編寫多個Filter,這些 Filter 組合起來稱之爲一個Filter鏈。spring
當第一個 Filter 的 doFilter 方法被調用時,web 服務器會建立一個表明 Filter 鏈的 FilterChain 對象傳遞給該方法。在 doFilter 方法中,若是調用了 FilterChain 對象的 doFilter 方法,則 web 服務器會檢 FilterChain 對象中是否還有 filter ,若是有,則調用第下一個 filter,若是沒有,則調用目標資源。數據庫
init() 方法和 destroy() 方法隨着項目的啓動和關閉纔會被調用,且僅一次。編程
web.xml 中spring-mvc
<!-- 定義Filter --> <filter> <!-- Filter的名字 --> <filter-name>demoFilter</filter-name> <!-- Filter的實現類 --> <filter-class>com.yule.common.filters.DemoFilter</filter-class> </filter> <!-- 定義Filter攔截的URL地址 --> <filter-mapping> <!-- Filter的名字 --> <filter-name>demoFilter</filter-name> <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求--> <url-pattern>/*</url-pattern> </filter-mapping>
Java 代碼服務器
package com.yule.common.filters; import javax.servlet.*; import java.io.IOException; /** * 過濾器 * @author yule * @date 2018/7/2 21:52 */ public class DemoFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("demo過濾器init。。。"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("demo過濾器doFilter。。。此處省略業務處理邏輯"); //經過判斷是否繼續往下走 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("demo過濾器destroy。。。"); } }
springMVC 攔截器源碼解析cookie
Spring Web MVC的處理器攔截器。相似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。攔截器是面向切面編程的,依賴的技術就是Java的動態代理。
日誌記錄:記錄請求日誌等
。
權限檢查:白名單等
;
性能監控:
能夠經過攔截器在進入處理器以前記錄開始時間,在處理完後記錄結束時間,從而獲得該請求的處理時間;
通用行爲:
讀取cookie獲得用戶信息並將用戶對象放入請求,從而方便後續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都須要的便可使用攔截器實現。
OpenSessionInView:
如Hibernate,在進入處理器打開Session,在完成後關閉Session。
本質是AOP(面向切面編程),符合 AOP 的全部功能均可以使用攔截器實現。
在 spring-mvc.xml 中
<mvc:interceptors> <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求 --> <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> --> <mvc:interceptor> <!--進行攔截的地址--> <mvc:mapping path="/**"/> <bean class="com.yule.common.interceptors.DemoInterceptor"/> </mvc:interceptor> </mvc:interceptors>
根據 xml 中的配置順序來執行。攔截器的執行順序在過濾器之間。
preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)
方法,該法在請求處理以前進行調用。SpringMVC 中的 Interceptor 是鏈式調用的,在一個應用中或者說是在一個請求中能夠同時存在多個 Interceptor 。每一個 Interceptor 的調用會依據它的聲明順序依次執行,並且最早執行的都是 Interceptor 中的 preHandle 方法,因此能夠在這個方法中進行一些前置初始化操做或者是對當前請求作一個預處理,也能夠在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值 Boolean 類型的,當它返回爲 false 時,表示請求結束,後續的 Interceptor 和 Controller 都不會再執行;當返回值爲 true 時,就會繼續調用下一個 Interceptor 的 preHandle 方法,若是已是最後一個 Interceptor 的時候,就會是調用當前請求的 Controller 中的方法。postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
方法,經過 preHandle 方法的解釋我們知道這個方法包括後面要說到的 afterCompletion 方法都只能在當前所屬的 Interceptor 的 preHandle 方法的返回值爲 true 的時候,才能被調用。postHandle 方法在當前請求進行處理以後,也就是在 Controller 中的方法調用以後執行,可是它會在 DispatcherServlet 進行視圖返回渲染以前被調用,因此我們能夠在這個方法中對 Controller 處理以後的 ModelAndView 對象進行操做。postHandle 方法被調用的方向跟 preHandle 是相反的,也就是說,先聲明的 Interceptor 的 postHandle 方法反而會後執行。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
方法,也是須要當前對應的 Interceptor 的 preHandle 方法的返回值爲 true 時纔會執行。所以,該方法將在整個請求結束以後,也就是在 DispatcherServlet 渲染了對應的視圖以後執行,這個方法的主要做用是用於進行資源清理的工做。afterCompletion 方法被調用的方向也跟 preHandle 是相反的,也就是說,先聲明的 Interceptor 的 afterCompletion 方法反而會後執行spring-mvc 中
<mvc:interceptors> <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求 --> <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> --> <mvc:interceptor> <!--進行攔截的地址--> <mvc:mapping path="/**"/> <bean class="com.yule.common.interceptors.DemoInterceptor"/> </mvc:interceptor> </mvc:interceptors>
Java 代碼
package com.yule.common.interceptors; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 自定義攔截器方式一 * Created by yule on 2018/7/2 22:37. */ public class DemoInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //return true 表示繼續下一個攔截器或者 control 層 //return false 表示被攔截下來 return false; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
package com.yule.common.interceptors; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 自定義攔截器方式二 * 通常都是經過實現HandlerInterceptor接口或者繼承HandlerInterceptorAdapter抽象類,複寫preHandle()、postHandle()和afterCompletion()這 3 個方法來對用戶的請求進行攔截處理 * Created by yule on 2018/7/2 22:43. */ public class Demo2Interceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } }
其實二者仍是有類似之處,就是均可以用做權限檢查、日誌記錄等狀況,可是在這些狀況下如何選擇就要知道不一樣之處。
使用範圍不一樣:Filter 只能用於 Web 程序中。而攔截器能夠用於 Web 程序,Application、Swing 程序中。
規範不一樣:Filter 是 servlet 規範規定的,是 servlet 支持的。而攔截器是在 spring 容器內,是 spring 框架支持的。
使用資源不一樣:Filter 不能直接使用 spring 的資源、對象等。而攔截器是一個 spring 組件,歸 spring 管理,配置在 spring 文件中,所以能使用 spring 的任何資源、對象,例如 Service 對象、數據源、事務管理等,經過 IoC 注入到攔截器便可。也就是說在攔截器中能夠注入一個 service ,用於業務邏輯或者訪問數據庫。
深度不一樣:Filter 只在 Servlet 先後起做用。而攔截器可以深刻到方法先後、異常拋出先後等,所以攔截器的使用具備更大的彈性。
做用範圍不一樣:攔截器只能對 Controller 層請求起做用,而過濾器則能夠對幾乎全部的請求起做用(如 .js、.css等)。
因此,在 Spring 構架的程序中,要優先使用攔截器。
攔截器是在過濾器之間運行的。
攔截器,spring-mvc.xml 中:
<mvc:interceptors> <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截全部的請求 --> <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> --> <mvc:interceptor> <!--進行攔截的地址--> <mvc:mapping path="/**"/> <bean class="com.yule.common.interceptors.DemoInterceptor"/> </mvc:interceptor> <mvc:interceptor> <!--進行攔截的地址--> <mvc:mapping path="/**"/> <bean class="com.yule.common.interceptors.Demo2Interceptor"/> </mvc:interceptor> </mvc:interceptors>
java 代碼:
package com.yule.common.interceptors; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 自定義攔截器方式一 * Created by yule on 2018/7/2 22:37. */ public class DemoInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //return true 表示繼續下一個攔截器或者 control 層 //return false 表示被攔截下來 System.out.println("preHandle"); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("afterCompletion"); } }
package com.yule.common.interceptors; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 自定義攔截器方式二 * 通常都是經過實現HandlerInterceptor接口或者繼承HandlerInterceptorAdapter抽象類,複寫preHandle()、postHandle()和afterCompletion()這 3 個方法來對用戶的請求進行攔截處理 * Created by yule on 2018/7/2 22:43. */ public class Demo2Interceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle 2222222..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle 22222222"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion 2222222"); } }
過濾器, web.xml 中:
<!-- 定義Filter --> <filter> <!-- Filter的名字 --> <filter-name>demoFilter</filter-name> <!-- Filter的實現類 --> <filter-class>com.yule.common.filters.DemoFilter</filter-class> </filter> <!-- 定義Filter攔截的URL地址 --> <filter-mapping> <!-- Filter的名字 --> <filter-name>demoFilter</filter-name> <!-- Filter負責攔截的URL 所有以/的請求,若是/*,將會全部的請求--> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>demo2Filter</filter-name> <filter-class>com.yule.common.filters.Demo2Filter</filter-class> </filter> <filter-mapping> <filter-name>demo2Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
java 代碼:
package com.yule.common.filters; import javax.servlet.*; import java.io.IOException; /** * 過濾器 * @author yule * @date 2018/7/2 21:52 */ public class DemoFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("demo過濾器init。。。"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("demo過濾器doFilter。。。此處省略業務處理邏輯"); //經過判斷是否繼續往下走 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("demo過濾器destroy。。。"); } }
package com.yule.common.filters; import javax.servlet.*; import java.io.IOException; /** * Created by yule on 2018/7/2 22:18. */ public class Demo2Filter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("demo2過濾器init 2222222"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("demo過濾器doFilter 222222"); //經過判斷是否繼續往下走 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("demo2過濾器destroy 22222 "); } }
調用 controller 打印結果: