亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Spring MVC 攔截器

1. 前言

說起 Spring MVC 框架的攔截器,就不得不提起原生 Servlet 中的過濾器,兩者的功能性質是一樣的,但底層實現機制會有差異性。攔截器用到了代理設計模式,是 Spring AOP 的具體實際應用。過濾器使用的是函數回調機制。

本節課程將和大家一起學習 Spring MVC 中的攔截器。通過本節課程,你將了解到攔截器的工作模式及基礎原理。重點是要掌握攔截器的實現過程。

2. 攔截器的概念

攔截器是什么?

攔截器就是一個功能模塊,工作模式決定了其與眾不同:

  • 非侵入式: Spring MVC 的攔截器是 AOP 編程思想的實際應用,可以在不影響服務對象代碼結構的條件下提供附加功能;
  • 動態裝配、拆卸: 需要時就裝配,不需要時可拆卸。與被服務的對象之間具有極低的耦合度。

2.1 攔截器的工作位置

Spring MVC 中的攔截器和 Servlet 中的過濾器的生命周期、以及服務的目標有差異性。過濾器的生命周期由服務器維護,當請求包進入服務器或響應包即將離開服務器時,過濾器將起作用。

過濾器是在 DispatcherServlet 之前或之后工作,攔截器是請求經過 DispatcherServlet 后進行攔截。

圖片描述

攔截器屬于 Spring MVC 組件,生命周期由 Spring 上下文容器對象維護。從細節上講,攔截器可以在用戶控制器之前、之后或視圖渲染完成之后行使攔截工作。

圖片描述

2.2 攔截器應用場景

攔截器可以解決很多實際問題:

  • 日志記錄: 記錄請求信息的日志,以便進行信息監控、信息統計、計算 PV(Page View)等;
  • 權限檢查: 如登錄檢測,進入處理器前檢測用戶是否登錄,如果沒有直接返回到登錄頁面;
  • 性能監控: 通過攔截器在進入處理器之前記錄開始時間,在處理完后記錄結束時間,從而得到該請求的處理時間;
  • 通用行為: 讀取 cookie 得到用戶信息并將用戶對象放入請求,從而方便后續流程使用,還有如提取 LocaleTheme 信息等只要是多個處理器都需要的即可使用攔截器實現
  • OpenSessionInView:Hibernate,在進入處理器打開 Session,在完成后關閉 Session。

除以上應用場景之外,你可以根據自己的業務需要靈活選擇。

3. 自定義攔截器

Spring MVC 內置有很多攔截器。這些攔截器提供的功能,基本上能夠滿足開發者完成常規開發。但是,需求總是瞬息變化的,開發者可以根據自己的業務需求自定義攔截器。

3.1 攔截器接口規范

自定義攔截器之前,首先要了解 Spring MVC 提供的攔截器接口,自定義攔截器必須遵循此接口規范。

public interface HandlerInterceptor {
	/**
	 * 用戶控制器之前攔截,實現用戶控制器數據的預處理工作,第三個參數為響應的用戶控制器
	 */
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
	/**
	 *對用戶控制器處理后的數據再進一步處理
	 */
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	/**
	 * 視圖解析器對 View 渲染完成后對最后結果進行處理
	 */
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}

Spring MVC 提供有攔截器適配器,適配器對攔截器接口做了簡單封裝。

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor{
	//……
}

3.2 自定義流程

開發者可以通過實現接口或繼承適配器這 2 種方式開發自己的攔截器。如自定義一個攔截器,監控控制器處理時間。

  1. 自定義攔截器類;
public class MyInterceptor extends HandlerInterceptorAdapter {
   	private long startTimer;
	private long endTimer;
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   	//調用控制器之前的時間
   	startTimer = System.currentTimeMillis();
	 return true;
   }
   
   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,   ModelAndView modelAndView) throws Exception {
        //控制器處理完成后的時間
     	endTimer = System.currentTimeMillis();
		System.out.println(endTimer-startTimer);
   }

   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      //輸出頁面渲染完成后的時間
      System.out.println("完成攔截器工作");
   }
}
  1. 通過攔截器配置告訴 Spring 它的存在。配置方式有 2 種:

注解方式: 打開 WebConfig 文件,添加如下代碼。

public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
	MyInterceptor myInterceptor = new MyInterceptor();
	registry.addInterceptor(myInterceptor).addPathPatterns("/student/*");
}

Tips: 此攔截器僅對 /student/* 請求地址有效。

xml 方式:

<mvc:interceptors>
     <mvc:interceptor>
         <mvc:mapping path="需要攔截的路徑"/>
        <bean class="自定義interceptor全路徑名">
 </mvc:interceptor>
</mvc:interceptors>

Tips:本課程以注解為主,XML 在這里只做簡要介紹。

  1. 編寫測試控制器;
@RequestMapping("/student")
public class StudentAction {
	@RequestMapping("/test")
	public String interceptor() {
		return "index";
	}
}
  1. 測試攔截是否能正常工作。
    發布項目,啟動 tomcat 服務器,在瀏覽器中輸入 http://localhost:8888/sm-demo/student/test ,查看控制臺上的輸出。
    圖片描述

3.3 攔截器鏈

所謂攔截器鏈,指多個攔截器一起協作工作,攔截器一起工作時,請注意攔截器中的各個方法之間的調用順序。

圖片描述

前面攔截器的 preHandle 方法的返回值會影響后面的攔截器和控制器是否正常工作:

  • 如果返回 true 表示繼續流程,可繼續調用下一個攔截器或進入控制器;
  • 如果返回 false 表示流程中斷,如登錄身份檢查失敗。不會繼續調用其他的攔截器或處理器。

Tips : 如果第一個攔截器的 preHandle 方法返回 true,則會進入第二個攔截器。如果第二個攔截器的 preHandle 方法 返回 false,則直接進入第一個攔截器的 afterCompletion 方法。

4. 小結

本章節和大家講解了 Spring MVC 的攔截器,需要大家掌握攔截的工作原理以及實現自定義攔截器。特別注意的是要理解多個攔截器在一起協作工作時方法之間是如何調用的。