1. 程式人生 > >四十一、比較Filter和Interceptor

四十一、比較Filter和Interceptor

Spring的Interceptor(攔截器)與Servlet的Filter有相似之處,都能實現許可權檢查、日誌記錄等。不同的是:

Filter Interceptor Summary
Filter 介面定義在 javax.servlet 包中 介面 HandlerInterceptor 定義在org.springframework.web.servlet 包中  
Filter 定義在 web.xml 中    
Filter在只在 Servlet 前後起作用。Filters 通常將 請求和響應(request/response) 當做黑盒子,Filter 通常不考慮servlet 的實現。 攔截器能夠深入到方法前後、異常丟擲前後等,因此攔截器的使用具有更大的彈性。允許使用者介入(hook into)請求的生命週期,在請求過程中獲取資訊,Interceptor 通常和請求更加耦合。 在Spring構架的程式中,要優先使用攔截器。幾乎所有 Filter 能夠做的事情, interceptor 都能夠輕鬆的實現1
Filter 是 Servlet 規範規定的。 而攔截器既可以用於Web程式,也可以用於Application、Swing程式中。 使用範圍不同
Filter 是在 Servlet 規範中定義的,是 Servlet 容器支援的。 而攔截器是在 Spring容器內的,是Spring框架支援的。 規範不同
Filter 不能夠使用 Spring 容器資源 攔截器是一個Spring的元件,歸Spring管理,配置在Spring檔案中,因此能使用Spring裡的任何資源、物件,例如 Service物件、資料來源、事務管理等,通過IoC注入到攔截器即可 Spring 中使用 interceptor 更容易
Filter 是被 Server(like Tomcat) 呼叫 Interceptor 是被 Spring 呼叫 因此 Filter 總是優先於 Interceptor 執行

interceptor 使用

interceptor 的執行順序大致為:

  1. 請求到達 DispatcherServlet
  2. DispatcherServlet 傳送至 Interceptor ,執行 preHandle
  3. 請求達到 Controller
  4. 請求結束後,postHandle 執行

Spring 中主要通過 HandlerInterceptor 介面來實現請求的攔截,實現 HandlerInterceptor 介面需要實現下面三個方法:

  • preHandle() – 在handler執行之前,返回 boolean 值,true 表示繼續執行,false 為停止執行並返回。
  • postHandle() – 在handler執行之後, 可以在返回之前對返回的結果進行修改
  • afterCompletion() – 在請求完全結束後呼叫,可以用來統計請求耗時等等

統計請求耗時

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();

		long executeTime = endTime - startTime;

		//modified the exisitng modelAndView
		modelAndView.addObject("executeTime",executeTime);

		//log it
		if(logger.isDebugEnabled()){
		   logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
		}
	}
}

例子來源 mkyong

使用mvc:interceptors標籤來宣告需要加入到SpringMVC攔截器鏈中的攔截器

<mvc:interceptors>  
<!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->  
<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標籤宣告一系列的攔截器,然後它們就可以形成一個攔截器鏈,攔截器的執行順序是按宣告的先後順序執行的,先宣告的攔截器中的preHandle方法會先執行,然而它的postHandle方法和afterCompletion方法卻會後執行。

在mvc:interceptors標籤下宣告interceptor主要有兩種方式:

  • 直接定義一個Interceptor實現類的bean物件。使用這種方式宣告的Interceptor攔截器將會對所有的請求進行攔截。
  • 使用mvc:interceptor標籤進行宣告。使用這種方式進行宣告的Interceptor可以通過mvc:mapping子標籤來定義需要進行攔截的請求路徑。

經過上述兩步之後,定義的攔截器就會發生作用對特定的請求進行攔截了。

Filter 使用

Servlet 的 Filter 介面需要實現如下方法:

  • void init(FilterConfig paramFilterConfig) – 當容器初始化 Filter 時呼叫,該方法在 Filter 的生命週期只會被呼叫一次,一般在該方法中初始化一些資源,FilterConfig 是容器提供給 Filter 的初始化引數,在該方法中可以丟擲 ServletException 。init 方法必須執行成功,否則 Filter 可能不起作用,出現以下兩種情況時,web 容器中 Filter 可能無效: 1)丟擲 ServletException 2)超過 web 容器定義的執行時間。
  • doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) – Web 容器每一次請求都會呼叫該方法。該方法將容器的請求和響應作為引數傳遞進來, FilterChain 用來呼叫下一個 Filter。
  • void destroy() – 當容器銷燬 Filter 例項時呼叫該方法,可以在方法中銷燬資源,該方法在 Filter 的生命週期只會被呼叫一次。

    FrequencyLimitFilter com.company.filter.FrequencyLimitFilter FrequencyLimitFilter /login/*

Filter 和 Interceptor 的一些用途

  • Authentication Filters
  • Logging and Auditing Filters
  • 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 可以:

  • 執行安全檢查 perform security checks
  • 格式化請求頭和主體 reformat request headers or bodies
  • 審查或者記錄日誌 audit or log requests
  • 根據請求內容授權或者限制使用者訪問 Authentication-Blocking requests based on user identity.
  • 根據請求頻率限制使用者訪問

Response Filters 可以:

  • 壓縮響應內容,比如讓下載的內容更小 Compress the response stream
  • 追加或者修改響應 append or alter the response stream
  • 建立或者整體修改響應 create a different response altogether
  • 根據地方不同修改響應內容 Localization-Targeting the request and response to a particular locale.

reference

  1. https://stackoverflow.com/a/8006315/1820217