攔截器概念
Java 里的攔截器是動態攔截action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前后執行的代碼,也可以在一個action執行前阻止其執行,同時也提供了一種可以提取action中可重用部分的方式。在 AOP(Aspect-Oriented Programming,面向切面編程)中攔截器用於在某個方法(包括構造器)或字段被訪問之前進行攔截,然后在之前或之后加入某些操作。特別地,現階段 Spring 自身僅支持基於方法的攔截操作!如果基於方法的攔截操作不能滿足需求,可以使用 AspectJ 與 Spring 進行集成,以實現更細粒度或更多方面的攔截操作。
常見應用場景
1、日志記錄:
記錄請求信息的日志,以便進行信息監控、信息統計、計算PV(Page View)等。
2、權限檢查:
如登錄檢測,進入處理器檢測檢測是否登錄,如果沒有直接返回到登錄頁面;
3、性能監控:
有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完后記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);
4、通用行為:
讀取cookie得到用戶信息並將用戶對象放入請求,從而方便后續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現。
5、OpenSessionInView:
如Hibernate,在進入處理器打開Session,在完成后關閉Session。
…………本質也是AOP(面向切面編程),也就是說符合橫切關注點的所有功能都可以放入攔截器實現。
攔截器接口( 通過實現HandlerInterceptor接口, 一般通過繼承HandlerInterceptorAdapter抽象類實現。)
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; }
preHandle:預處理回調方法,實現處理器的預處理(如登錄檢查),第三個參數為響應的處理器(如我們上一章的Controller實現);
返回值:true表示繼續流程(如調用下一個攔截器或處理器);
false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
postHandle:后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
afterCompletion:整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中preHandle返回true的攔截器的afterCompletion。
實戰代碼:
工程結構
springmvc.xml spring的配置
定義三個攔截器 AllInterceptor 是全局攔截器, Interceptor1、 Interceptor2是獨立的攔截器使用
package com.spring.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class AllInterceptor extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("===========AllInterceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("===========AllInterceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("===========AllInterceptor afterCompletion"); } }
package com.spring.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class Interceptor1 extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("===========Interceptor1 preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("===========Interceptor1 postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("===========Interceptor1 afterCompletion"); } }
package com.spring.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class Interceptor2 extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("===========Interceptor2 preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("===========Interceptor2 postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("===========Interceptor2 afterCompletion"); } }
Controller層 TestController1.java
package com.spring.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("interceptor") public class TestController1 { @RequestMapping("test1") public void test1() { System.out.println("Controller test1 !"); } @RequestMapping("test2") public void test2() { System.out.println("Controller test2 !"); } }
spring相關配置
<!-- 掃描含有@Controller注解 --> <context:component-scan base-package="com.spring.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<!-- 使用mvc:interceptors標簽來聲明需要加入到SpringMVC攔截器鏈中的攔截器 --> <mvc:interceptors> <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 --> <bean class="com.spring.interceptor.AllInterceptor"/> <mvc:interceptor> <mvc:mapping path="/interceptor/test1"/> <!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 --> <bean class="com.spring.interceptor.Interceptor1"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/interceptor/test2"/> <bean class="com.spring.interceptor.Interceptor2"/> </mvc:interceptor> </mvc:interceptors>
測試結果:
url: http://localhost:8080/dynamicdatasource/interceptor/test1
url: http://localhost:8080/dynamicdatasource/interceptor/test2
另外 對已攔截器的配置,有時候需要是對全部請求url進行攔截,有時候對一些url不進行攔截,可以采用下面幾種方式;
<mvc:interceptors> <bean id="allInterceptor" class="com.base.interceptor.AllInterceptor"/> <mvc:interceptor> <mvc:mapping path="/*/**" /> <bean id="CommonInterceptor" class="com.base.interceptor.CommonInterceptor"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/dical/**"/> //攔截的url <mvc:mapping path="/admin/**"/>//攔截的url <mvc:exclude-mapping path="/admin/login"/> //不攔截的url <mvc:exclude-mapping path="/admin/main"/>//不攔截的url <bean id="DicalInterceptor" class="com.base.interceptor.DicalInterceptor"/> </mvc:interceptor> </mvc:interceptors>
或者:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/bank/modifCode/**"/> <mvc:exclude-mapping path="/bank/tialize/**"/> <mvc:exclude-mapping path="/bank/**"/> <bean class="com.intercr.MthoduestIneptor" /> </mvc:interceptor> </mvc:interceptors>
說明:
1)mvc:mapping 攔截器路徑配置
2)mvc:exclude-mapping 攔截器不需要攔截的路徑