1 攔截器
1.1 簡介
Spring MVC
的攔截器
Interceptor
類似于
Servlet
中的過濾器
Filter
,用于對處理器進行預處理和後處理.
1.2 實作原理
攔截器
Interceptor
的攔截功能是基于
Java
的動态代理來實作的.
1.3 應用場景
- 日志記錄:記錄請求資訊的日志,以便進行資訊監控、資訊統計、計算PV(Page View)等.
- 權限檢查:如登入檢測,進入處理器檢測檢測是否登入,如果沒有直接傳回到登入頁面.
- 性能監控:有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完後記錄結束時間,進而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄).
- 通用行為:讀取cookie得到使用者資訊并将使用者對象放入請求,進而友善後續流程使用,還有如提取Locale、Theme資訊等,隻要是多個處理器都需要的即可使用攔截器實作.
- OpenSessionInView:如Hibernate,在進入處理器打開Session,在完成後關閉Session。
攔截器的本質也是AOP(面向切面程式設計), 也就是說符合橫切關注點的所有功能都可以放入攔截器實作.
2 Spring MVC 提供的攔截器接口和類
Spring MVC 提供了一個攔截器接口
HandlerInterceptor
和一個攔截器擴充卡抽象類
HandlerInterceptorAdapter
.
2.1 HandlerInterceptor
接口
HandlerInterceptor
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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;
}
在
HandlerInterceptor
接口中,定義了 3 個方法,分别為
preHandle()
、
postHandle()
和
afterCompletion()
,自定義攔截器需要實作這個接口并且要全部實作這三個方法.
-
方法,該方法在請求處理之前進行調用。SpringMVC 中的 Interceptor 是鍊式調用的,在一個應用中或者說是在一個請求中可以同時存在多個 Interceptor 。每個 Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是 Interceptor 中的 preHandle 方法,是以可以在這個方法中進行一些前置初始化操作或者是對目前請求做一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的傳回值是布爾值 Boolean 類型的,當它傳回為 false 時,表示請求結束,後續的 Interceptor 和 Controller 都不會再執行;當傳回值為 true 時,就會繼續調用下一個 Interceptor 的 preHandle 方法,如果已經是最後一個 Interceptor 的時候,就會是調用目前請求的 Controller 中的方法。preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)
-
方法,通過 preHandle 方法的解釋咱們知道這個方法包括後面要說到的 afterCompletion 方法都隻能在目前所屬的 Interceptor 的 preHandle 方法的傳回值為 true 的時候,才能被調用。postHandle 方法在目前請求進行處理之後,也就是在 Controller 中的方法調用之後執行,但是它會在 DispatcherServlet 進行視圖傳回渲染之前被調用,是以咱們可以在這個方法中對 Controller 處理之後的 ModelAndView 對象進行操作。postHandle 方法被調用的方向跟 preHandle 是相反的,也就是說,先聲明的 Interceptor 的 postHandle 方法反而會後執行。postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
-
方法,也是需要目前對應的 Interceptor 的 preHandle 方法的傳回值為 true 時才會執行。是以,該方法将在整個請求結束之後,也就是在 DispatcherServlet 渲染了對應的視圖之後執行,這個方法的主要作用是用于進行資源清理的工作。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
2.2 HandlerInterceptorAdapter
抽象類
HandlerInterceptorAdapter
有時候我們可能隻需要實作三個回調方法中的某一個,如果實作
HandlerInterceptor
接口的話,三個方法必須實作,不管你需不需要. 此時spring提供了一個
HandlerInterceptorAdapter
擴充卡(一種擴充卡設計模式的實作),允許我們隻實作需要的回調方法.
package org.springframework.web.servlet.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* This implementation is empty.
*/
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
3 練習
首先定義兩個Interceptor的實作.
HandlerInterceptor1
package com.tao.springmybatispostgresql.interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HandlerInterceptor1 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=============== HandlerInterceptor1 preHandle ================");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=============== HandlerInterceptor1 postHandle ================");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=============== HandlerInterceptor1 afterCompletion ================");
}
}
HandlerInterceptor2
package com.tao.springmybatispostgresql.interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=============== HandlerInterceptor2 preHandle ================");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=============== HandlerInterceptor2 postHandle ================");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=============== HandlerInterceptor2 afterCompletion ================");
}
}
編寫
InterceptorController
package com.tao.springmybatispostgresql.web.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/interceptor")
public class InterceptorController {
@GetMapping("/handleReq")
public String handleReq() {
System.out.println("=========== InterceptorController ");
return "handleReq success.";
}
}
配置檔案
spring-interceptor.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:interceptors>
<!-- 使用 bean 定義一個 Interceptor,直接定義在 mvc:interceptors 下面的 Interceptor 将攔截所有的請求 -->
<bean class="com.tao.springmybatispostgresql.interceptor.HandlerInterceptor1"/>
<!-- mvc:interceptor下可以針對具體的url進行 Interceptor 的配置 -->
<mvc:interceptor>
<mvc:mapping path="/interceptor/handleReq"/>
<!-- 定義在 mvc:interceptor 下面的 Interceptor,表示對特定的請求進行攔截 -->
<bean class="com.tao.springmybatispostgresql.interceptor.HandlerInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
4 測試
當我們通路一個url的時候( 不是
/interceptor/handleReq
), 隻有
HandlerInterceptor1
起作用.
當我們通路
/interceptor/handleReq
的時候,
HandlerInterceptor1
和
HandlerInterceptor2
都起作用.
參考:
詳述 Spring MVC 架構中攔截器 Interceptor 的使用方法
SpringMVC 攔截器