SpringMVC 中的HandlerInterceptor 攔截器類似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。
(如無特殊說明,下文所說的攔截器即處理器攔截器)
SpringMVC 中的Interceptor 攔截請求是通過HandlerInterceptor 來實現的。在SpringMVC 中定義一個Interceptor 非常簡單,主要有兩種方式,第一種方式是要定義的Interceptor類要實現了Spring 的HandlerInterceptor 接口,或者是這個類繼承實現了HandlerInterceptor 接口的類,比如Spring 已經提供的實現了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter ;第二種方式是實現Spring的WebRequestInterceptor接口,或者是繼承實現了WebRequestInterceptor的類。
1.攔截器接口
先看下面的攔截器接口:
package org.springframework.web.servlet;
public interface HandlerInterceptor {
boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception;
}
我們可能注意到攔截器一個有3個回調方法,而一般的過濾器Filter才兩個,這是怎麼回事呢?馬上分析。
preHandle:預處理回調方法,實現處理器的預處理(如登錄檢查),第三個參數爲響應的處理器(如:Controller實現);
preHandle方法是進行處理器攔截用的,顧名思義,該方法將在Controller處理之前進行調用,
* SpringMVC中的Interceptor攔截器是鏈式的,可以同時存在多個Interceptor,
* 然後SpringMVC會根據聲明的前後順序一個接一個的執行,
* 而且所有的Interceptor中的preHandle方法都會在Controller方法調用之前調用。
* SpringMVC的這種Interceptor鏈式結構也是可以進行中斷的,
* 這種中斷方式是令preHandle的返回值爲false,當preHandle的返回值爲false的時候整個請求就結束了。
返回值:true表示繼續流程(如調用下一個攔截器或處理器);
false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
postHandle:後處理回調方法,實現處理器的後處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能爲null。
* 這個方法只會在當前這個Interceptor的preHandle方法返回值爲true的時候纔會執行。 * postHandle是進行處理器攔截用的,它的執行時間是在處理器進行處理之後, 也就是在Controller的方法調用之後執行, * 但是它會在DispatcherServlet進行視圖的渲染之前執行,也就是說在這個方法中你可以對ModelAndView進行操作。 * 這個方法的鏈式結構跟正常訪問的方向是相反的,也就是說先聲明的Interceptor攔截器該方法反而會後調用。
afterCompletion:整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中preHandle返回true的攔截器的afterCompletion。
* 該方法也是需要當前對應的Interceptor的preHandle方法的返回值爲true時纔會執行。
* 該方法將在整個請求完成之後,也就是DispatcherServlet渲染了視圖執行, 這個方法的主要作用是用於清理資源的。
2.攔截器適配器
有時候我們可能只需要實現三個回調方法中的某一個,如果實現
HandlerInterceptor接口的話,三個方法必須實現,不管你需不需要,此時spring提供了一個HandlerInterceptorAdapter適配器(一種適配器設計模式的實現),允許我們只實現需要的回調方法。
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
//省略代碼 此處所以三個回調方法都是空實現,preHandle返回true。
}
正常運行流程圖:
中斷流程圖:
中斷流程中,比如是HandlerInterceptor4中斷的流程(preHandle返回false),此處僅調用它之前攔截器HandlerInterceptor3的preHandle返回true的afterCompletion方法。
3.配置攔截器
使用mvc:interceptors標籤來聲明需要加入到SpringMVC攔截器鏈中的攔截器。
在springMVC.xml配置文件增加:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/static/**" />
<bean class="攔截器java代碼路徑" />
</mvc:interceptor>
</mvc:interceptors>
說明:
1)mvc:mapping 攔截器路徑配置
2)mvc:exclude-mapping 攔截器不需要攔截的路徑
示例:
上面的示例可以看出可以利用mvc:interceptors標籤聲明一系列的攔截器,然後它們就可以形成一個攔截器鏈,攔截器的執行順序是按聲明的先後順序執行的,先聲明的攔截器中的preHandle方法會先執行,然而它的postHandle方法和afterCompletion方法卻會後執行。
4.示例
下面是一個登錄攔截器的例子:
package cn.wmyskxz.interceptor;
import cn.wmyskxz.pojo.User;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
/**
* 登錄攔截器:
*/
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
String[] noNeedAuthPage = new String[]{
"/home",
"/searchProduct",
"/sortProduct",
"/showProduct",
"/loginPage",
"/login",
"/registerPage",
"/register",
"/registerSuccessPage",
"/test",
"/checkLogin",
"/admin"
};
String uri = request.getRequestURI();
if (!Arrays.asList(noNeedAuthPage).contains(uri)) {
User user = (User) session.getAttribute("user");
if (null == user) {
response.sendRedirect("/loginPage");
return false;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}
該攔截器作用就是對於所有不需要授權的請求(字符串數組noNeedAuthPage中列出的),正常執行Controller,否則重定向到loginPage頁面。