SpringMVC攔截器源碼解析

 前言:提到攔截器,我們一定會想到Spring Aop。在看SpringMVC源碼以前,一直以爲SpringMVC的攔截器是用Spring Aop的動態代理來實現,並且也很困惑,如果用動態代理如何實現攔截器?這裏就不介紹Spring Aop實現攔截器的方式,剖析SpringMVC 攔截器實現的細節。

    SpringMVC攔截器也是Aop(面向切面)思想構建,但不是SpringAop動態代理實現的,主要採用責任鏈和適配器的設計模式來實現,直接嵌入到SpringMVC入口代碼裏面。

    涉及到設計模式,有利於對代碼的理解

        責任鏈模式:https://segmentfault.com/a/1190000012079496

        適配器模式:https://www.cnblogs.com/V1haoge/p/6479118.html

一、攔截器的接口類結構

攔截器的頂級接口爲HandleInterceptor,提供三個接口,分別是preHandle(請求前)、postHandle(請求提交)、afterCompletion(請求完成後攔截)

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
/**
*  攔截器頂級接口
*/
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;

}

而我們通常使用的是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 {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		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 {
	}
	/**
	 * This implementation is empty.
     * 來自實現AsyncHandlerInterceptor 的方法
	 */
	@Override
	public void afterConcurrentHandlingStarted(
			HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	}

}

二、攔截器源碼運行的調用流程

    1. HandlerMapping初始化

瞭解到SpringMVC攔截器接口HandlerInterceptorAdapter,我們再進入SpringMVC請求入口org.springframework.web.servlet.DispatcherServlet,攔截器運作工作流程由此展開。DispatcherServlet作爲控制中心,攔截器就嵌入在入口方法中doDispatch中。SpringMVC又是如何將定義的攔截器加入進去呢?


	/**
	 * 完成Servlet一系列初始化工作,包括視圖解析器、處理器映射器、處理器適配器、主題解析器...
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
        // 初始化RequestMapping,攔截器是作用在RequestMapping
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

 

初始化RequestMapping的時候,獲取到RequestMapping的集合,因爲攔截器作用在單個RequestMapping,來實現攔截器鏈。這裏就不詳細介紹RequestMapping的獲取。

    2. DispatcherServlet執行調用,doService作爲Servlet主要執行者,通過調用doDispatch來真正執行請求處理

在initStrategies(ApplicationContext context)完成初始化準備工作後,來正式進入DispatcherServlet請求進入的處理過程。DispatcherServlet基於Servlet開發,它的執行方式是 doService(HttpServletRequest request, HttpServletResponse response) 

   /**
    *  doService做了一部分請求準備預處理的工作, 而實際處理的工作調用doDispatch(request, response)
    */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// 對請求屬性快照的保持和存儲後的使用,
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		//對SpringMVC框架中處理器Handle和視圖渲染,提前做準備工作
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
        // FlashMap是對重定向的參數傳遞的處理
		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
           //***請求的實際處理邏輯***-----進入方法
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

    3. doDispatch中完成攔截器的添加和攔截器攔截處理

 doService做了一部分請求準備預處理的工作,包括請求中設置處理器和視圖的屬性,作用於後續處理器的運作和視圖渲染, 而實際請求邏輯處理的工作調用doDispatch(request, response),也是我們需要重點跟進的代碼。或許大家初次看doDispatch方法中的代碼,還不知道攔截器在哪裏運作。

處理流程是:

  1.     通過getHandler(HttpServletRequest request)獲取到HandlerExecutionChain處理器執行鏈,將攔截器注入到HandlerExecutionChain屬性
  2.   分別調用HandlerExecutionChain的三個方法,applyPreHandle、applyPostHandle、triggerAfterCompletion,實現前置攔截/請求提交攔截和請求完成後攔截。使用責任鏈的設計模式,實際調用的是HandleInterceptor的三個接口,分別對應preHandle、postHandle、afterCompletion
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);
				//***獲取到請求執行鏈,攔截器的添加在裏面完成***---進入方法
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				// 獲取到相應處理器適配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
                //1.請求前攔截處理---進入方法細看(責任鏈模式,調用攔截器鏈的執行)
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
                //2.請求提交的攔截處理---進入方法細看(責任鏈模式,調用攔截器鏈的執行)
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
            // 3.請求結果視圖渲染完成後,調用mappedHandler.triggerAfterCompletion(),實現請求完成後攔截
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

 

三、結合責任鏈設計在攔截器中的運用

    責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係, 將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理他爲止。

分析 1、如何將攔截器添加到HandlerExecutionChain中

    首先遍歷HandlerMappings集合,HandlerMappings是在initHandlerMappings(context)初始化得到。根據request查找到相應 HandlerMapping的HandlerExecutionChain。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //遍歷處理器映射器集合,找到相應請求的處理器映射器
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
            //***請求匹配到映射器,並返回HandlerExecutionChain***---進入方法細看
            //----攔截器的添加在方法裏面完成
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}
@Override
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
        //***獲取HandlerExecutionChain,攔截器的添加 ***---進一步跟進代碼
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}
/**
 * 走到這裏,才真正看到了HandlerExecutionChain的創建和攔截器的添加
 */
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        //獲取到HandlerExecutionChain 
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        //遍歷攔截器,adaptedInterceptors也是在initHandlerMapping初始化得到
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    //將攔截器添加
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

分析2、如何執行多個攔截器,通過責任鏈模式

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
/**
	 * Apply postHandle methods of registered interceptors.
	 */
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}
/**
	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
	 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
	 * has successfully completed and returned true.
	 */
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章