1. 程式人生 > >11.SpringMVC 請求處理

11.SpringMVC 請求處理

基本概念

SpringMVC 通過 HandlerAdapterhandler 方法來呼叫請求處理函式。

在 DispatcherServlet 中根據請求路徑利用 Handlermapping 找到對應的 handler 後,首先檢查當前 Ioc 容器中所有可用的 HandlerAdapter ,再利用 HandlerAdapter 中的 supports 方法找到可以使用的HandlerAdapter

首先來看它的繼承關係:

這裡寫圖片描述

內部構造

該介面內部定義了定義了三個方法。

public interface HandlerAdapter {
    // 是否支援該 HandlerMethod 
boolean supports(Object handler); // 根據 HandlerMethod 取得 ModelAndView ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); }

AbstractHandlerMethodAdapter

該類是 HandlerAdapter 介面的簡單抽象類,實現了介面定義的方法。

但也並未做真正的實現,而是留給了子類。下面來看它的原始碼:

public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}


public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, 
    Object handler) throws
Exception { return handleInternal(request, response, (HandlerMethod) handler); } public final long getLastModified(HttpServletRequest request, Object handler) { return getLastModifiedInternal(request, (HandlerMethod) handler); }

RequestMappingHandlerAdapter

該類繼承了 AbstractHandlerMethodAdapter 類,真正意義上實現了 HandlerAdapter 介面定義的功能。

1.supportsInternal

預設返回 true,說明只要處理器是 HandlerMethod 類即可

protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}

2.getLastModifiedInternal

預設返回 -1.

protected long getLastModifiedInternal(HttpServletRequest request, 
    HandlerMethod handlerMethod) {
    return -1;
}

3.handleInternal

該方法負責呼叫 HandlerMethod(處理器) ,並返回 ModelAndView 。

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, 
    HandlerMethod handlerMethod) throws Exception {

    // 1.校驗請求
    // 檢查是否支援當前 rqeuest 的 method 和 session
    checkRequest(request);

    // 2.判斷控制器是否存在 @SessionAttributes 註解
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        // 2.1設定快取
        applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    } else {
        // 2.2準備響應
        prepareResponse(response);
    }

    // 預設為 false,為 true 表示在同步塊中執行 invokeHandlerMethod
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return invokeHandlerMethod(request, response, handlerMethod);
            }
        }
    }

    // 關鍵 -> 3.處理器呼叫
    return invokeHandlerMethod(request, response, handlerMethod);
}

分析程式碼,該方法的具體過程如下:

  • 1.校驗請求,即檢查是否支援當前 rqeuest 的 method 和 session
  • 2.判斷控制器是否存在 @SessionAttributes 註解,有則設定快取,否則準備響應
  • 3.處理器呼叫,返回 ModelAndView 。

ModelMap

該類繼承自 LinkedHashMap,說明它代表一組有序的對映集,用於儲存資料。

它的簽名如下:

public class ModelMap extends LinkedHashMap<String, Object> 

再來看它的建構函式:

public ModelMap(String attributeName, Object attributeValue) {
    addAttribute(attributeName, attributeValue);
}
public ModelMap addAttribute(String attributeName, Object attributeValue) {
    // 儲存 <K,V>
    put(attributeName, attributeValue);
    return this;
}

ModelAndView

該類內部儲存了 ModelMap 和 View。

private Object view;
private ModelMap model;

public ModelAndView(String viewName, String modelName, Object modelObject) {
    this.view = viewName;
    addObject(modelName, modelObject);
}

public ModelAndView addObject(String attributeName, Object attributeValue) {
    getModelMap().addAttribute(attributeName, attributeValue);
    return this;
}

public ModelMap getModelMap() {
    if (this.model == null) {
        this.model = new ModelMap();
    }
    return this.model;
}

ModelAndViewContainer

該類表示一個容器,不僅包含了 ModelMap 、View 還有其他相關內容。

觀察它的成員變數可知:

private Object view;

private final ModelMap defaultModel = new BindingAwareModelMap();

private ModelMap redirectModel;

private final SessionStatus sessionStatus = new SimpleSessionStatus();

private boolean redirectModelScenario = false;

private boolean ignoreDefaultModelOnRedirect = false;

private boolean requestHandled = false;

ServletInvocableHandlerMethod

1.基本概念

該類實現了 HandlerMethod 類,具體繼承關係如下:

這裡寫圖片描述

下面來看它的建構函式:

public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
    super(handlerMethod);
    initResponseStatus();
}

觀察程式碼,該類的建構函式執行過程如下:

  • 呼叫父類,即 HandlerMethod 的建構函式 ,將 HandlerMethod 的成員變數賦值給自己的成員變數。

    protected HandlerMethod(HandlerMethod handlerMethod) {
        this.bean = handlerMethod.bean;
        this.beanFactory = handlerMethod.beanFactory;
        this.beanType = handlerMethod.beanType;
        this.method = handlerMethod.method;
        this.bridgedMethod = handlerMethod.bridgedMethod;
        this.parameters = handlerMethod.parameters;
        this.resolvedFromHandlerMethod = 
            handlerMethod.resolvedFromHandlerMethod;
    }
  • 提取 @ResponseStatus 註解的相關內容賦值給自己的成員變數

    private void initResponseStatus() {
        ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
        if (annotation != null) {
            this.responseStatus = annotation.code();
            this.responseReason = annotation.reason();
        }
    }

2. invokeAndHandle

關鍵來看該類的 invokeAndHandle 方法, 它負責呼叫 HandlerMethod 中控制器的指定方法。

public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer, 
Object... providedArgs) throws Exception {

    // 關鍵 -> 1.執行控制器指定方法
    Object returnValue = 
        invokeForRequest(webRequest, mavContainer, providedArgs);

    // 2.設定響應狀態
    setResponseStatus(webRequest);

    // 3.判斷請求是否處理完畢,根據返回值來判斷。
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || 
            hasResponseStatus() || 
            mavContainer.isRequestHandled()) {

            // 為 true ,表示方法呼叫完畢
            mavContainer.setRequestHandled(true);
            return;
        }
    }else if (StringUtils.hasText(this.responseReason)) {
        mavContainer.setRequestHandled(true);
        return;
    }

    // 存在返回值時,可能還需進行檢視解析
    mavContainer.setRequestHandled(false);

    try {
        // 處理返回值,在 MavContainer 中設定 ViewName
        // 並判斷其是不是【重定向請求】
        this.returnValueHandlers.handleReturnValue(
            returnValue, 
            getReturnValueType(returnValue), 
            mavContainer, 
            webRequest);

    }catch (Exception ex) {
        // 丟擲異常...
    }
}
  • 執行控制器指定方法
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    // 省略部分程式碼...

    // 取得方法入參值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

    // 利用反射執行該方法,並取得返回值
    Object returnValue = doInvoke(args);
    return returnValue;
}

protected Object doInvoke(Object... args) throws Exception {

    // 設定方法的訪問許可權,BridgedMethod 表示控制器類的指定方法
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        // 關鍵 -> 利用反射執行該方法,Bean 表示控制器類名稱
        return getBridgedMethod().invoke(getBean(), args);

    }catch (IllegalArgumentException ex) {
        // 丟擲異常...
    }catch (InvocationTargetException ex) {
        // 丟擲異常...
    }
}

處理器呼叫

接下里分析下 handleInternal 方法中的處理器呼叫

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, 
    HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);

    // 1.資料繫結
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

    // 2.建立 ModelFactory 
    // 添加了 @ModelAttribute,@SessionAttributes 等註解內容
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    // 3.建立 ServletInvocableHandlerMethod,並繫結相關屬性
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

    // 4.建立 ModelAndViewContainer
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

    // 省略部分原始碼...

    // 5.呼叫控制器方法
    invocableMethod.invokeAndHandle(webRequest, mavContainer);


    // 6.返回 ModelAndView
    return getModelAndView(mavContainer, modelFactory, webRequest);
}

接著來看該類的 getModelAndView 方法:

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, 
    NativeWebRequest webRequest) throws Exception {

    // 更新 Mdoel 
    modelFactory.updateModel(webRequest, mavContainer);

    // 判斷請求是否處理器完畢
    if (mavContainer.isRequestHandled()) {
        return null;
    }

    // 關鍵 -> 建立 ModelAndView
    ModelMap model = mavContainer.getModel();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);

    // 判斷 view 是不是字串
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }

    // 重定向相關
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
    }

    return mav;
}

總結

下面來看 HandlerAdapter 的工作流程圖:

這裡寫圖片描述