HandlerInterceptor 處理器攔截器的用法

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頁面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章