一、過濾器的作用及原理
Servlet中的過濾器Filter是實現了javax.servlet.Filter接口的服務器端程序,主要的用途是設置字符集、控制權限、控制轉向、做一些業務邏輯判斷等。在web.xml文件配置過濾器參數,在用戶請求時會攔截到請求,可以對對請求或響應(Request、Response)統一設置編碼,簡化操作,同時還可進行邏輯判斷,如用戶是否已經登陸、有沒有權限訪問該頁面等等工作。如下圖,Filter可以理解成一種Servlet,主要用于對用戶請求進行預處理,也可以對HttpServletResponse進行后處理。
二、過濾器的簡單實現
ilter接口中有一個doFilter方法,當我們編寫好Filter,并配置對哪個web資源進行攔截后,WEB服務器每次在調用web資源的service方法之前,都會先調用一下filter的doFilter方法,因此,在該方法內編寫代碼可達到如下目的:
1、調用目標資源之前,讓一段代碼執行。
2、是否調用目標資源(即是否讓用戶訪問web資源)。
3、調用目標資源之后,讓一段代碼執行。
web服務器在調用doFilter方法時,會傳遞一個filterChain對象進來,filterChain對象是filter接口中最重要的一個對 象,它也提供了一個doFilter方法,開發人員可以根據需求決定是否調用此方法,調用該方法,則web服務器就會調用web資源的service方 法,即web資源就會被訪問,否則web資源不會被訪問。
package me.gacl.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* @ClassName: FilterDemo01
* @Description:filter的三種典型應用:
* 1、可以在filter中根據條件決定是否調用chain.doFilter(request, response)方法,
* 即是否讓目標資源執行
* 2、在讓目標資源執行之前,可以對request\response作預處理,再讓目標資源執行
* 3、在目標資源執行之后,可以捕獲目標資源的執行結果,從而實現一些特殊的功能
* @author: 孤傲蒼狼
* @date: 2014-8-31 下午10:09:24
*/
public class FilterDemo01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----過濾器初始化----");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//對request和response進行一些預處理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterDemo01執行前!!!");
chain.doFilter(request, response); //讓目標資源執行,放行
System.out.println("FilterDemo01執行后!!!");
}
@Override
public void destroy() {
System.out.println("----過濾器銷毀----");
}
}
三、攔截器的作用及原理
攔截器是在面向切面編程中應用的,就是在你的service或者一個方法前調用一個方法,或者在方法后調用一個方法,是基于JAVA的反射機制實現的。 攔截器將Action共用的行為獨立出來,在Action執行前后執行。這也就是我們所說的AOP,它是分散關注的編程方法,它將通用需求功能從不相關類之中分離出來;同時,能夠共享一個行為,一旦行為發生變化,不必修改很多類,只要修改這個行為就可以。當你提交對Action(默認是.action結尾的url)的請求時,ServletDispatcher會根據你的請求,去調度并執行相應的Action。在Action執行之前,調用被Interceptor截取,Interceptor在Action執行前后執行。
SpringMVC 中的Interceptor 攔截請求是通過HandlerInterceptor 來實現的。在SpringMVC 中定義一個Interceptor 非常簡單,主要有兩種方式,第一種方式是要定義的Interceptor類要實現了Spring 的HandlerInterceptor 接口,或者是這個類繼承實現了HandlerInterceptor 接口的類,比如Spring 已經提供的實現了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter ;第二種方式是實現Spring的WebRequestInterceptor接口,或者是繼承實現了WebRequestInterceptor的類。
四、攔截器簡單實現
攔截器主要實現以下三個方法:
1、preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean類型的,當它返回為false 時,表示請求結束,后續的Interceptor 和Controller 都不會再執行;當返回值為true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最后一個Interceptor 的時候就會是調用當前請求的Controller 方法。
2、postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括后面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之后,也就是Controller 方法調用之后執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會后執行,這和Struts2 里面的Interceptor 的執行過程有點類型。Struts2 里面的Interceptor 的執行過程也是鏈式的,只是在Struts2 里面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然后每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之后的內容就是反向的。
3、afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之后,也就是在DispatcherServlet 渲染了對應的視圖之后執行。這個方法的主要作用是用于進行資源清理工作的。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("攔截器啟動!!");
//獲取請求的URL
String url = request.getRequestURI();
System.out.println(url);
//URL:login.jsp是公開的;這個demo是除了login.jsp是可以公開訪問的,其它的URL都進行攔截控制
if(url.indexOf("AnalogLogin")>=0){
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info(ConfigConstants.ExecutionOrderTwo);
//System.out.println(TestInterface.findPrison().toString());
if(modelAndView != null){ //加入當前時間
modelAndView.addObject("var", ConfigConstants.TestPostHandle);
}
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
log.info(ConfigConstants.ExecutionOrderThree);
}
}
五、攔截器(Interceptor)和過濾器(Filter)的區別
Spring的Interceptor(攔截器)與Servlet的Filter有相似之處,比如二者都是AOP編程思想的體現,都能實現權限檢查、日志記錄等。以下是它們的不同之處:
執行順序如下: