Spring MVC 核心調用流程 [十六] Spring MVC 核心調用流程

[十六] Spring MVC 核心調用流程

DispatcherServlet 是什麼

::: tip 概述
DispatcherServlet作爲Spring MVC的核心控制器,初始化組件,處理客戶端發送的請求,並返回ModelAndView,進行視圖渲染。主要是實現了父類FrameworkServlet的抽象方法doService()
:::

DispatcherServlet 類圖

DispatcherServletinitStrategies方法如何被調用的

入口類: AbstractApplicationContext #refresh

  • ├─ refresh <Badge text="① AbstractApplicationContext" type="danger"/>
  • │ ├─ finishRefresh <Badge text="② AbstractApplicationContext" type="danger"/>
  • │ │ └─ publishEvent <Badge text="③ AbstractApplicationContext" type="danger"/>
  • │ │ │ ├─ publishEvent <Badge text="④ ApplicationEventPublisher" type="danger"/>
  • │ │ │ └─ multicastEvent <Badge text="⑤ SimpleApplicationEventMulticaster" type="danger"/>
  • │ │ │ │ └─ invokeListener <Badge text="⑥ SimpleApplicationEventMulticaster" type="danger"/>
  • │ │ │ │ │ └─ doInvokeListener <Badge text="⑦ SimpleApplicationEventMulticaster" type="danger"/>
  • │ │ │ │ │ │ └─ onApplicationEvent <Badge text="⑧ FrameworkServlet的內部類ContextRefreshListener" type="danger"/>
  • │ │ │ │ │ │ │ └─ onRefresh <Badge text="⑨ FrameworkServlet" type="danger"/>
  • │ │ │ │ │ │ │ │ └─ onRefresh <Badge text="⑩ DispatcherServlet" type="danger"/>
  • │ │ │ │ │ │ │ │ │ └─ initStrategies <Badge text="⑪ DispatcherServlet" type="danger"/>

進入onRefresh方法

所在類:org.springframework.web.servlet.DispatcherServlet

    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        // 初始化文件上傳處理
        initMultipartResolver(context);
        // 初始化本地化 Resolver
        initLocaleResolver(context);
        // 初始化主題 Resolver
        initThemeResolver(context);
        // 初始化 URL映射關係
        initHandlerMappings(context);
        // 初始化Handler接口適配器
        initHandlerAdapters(context);
        // 初始化異常處理的 handler
        initHandlerExceptionResolvers(context);
        // 初始化請求路徑轉換
        initRequestToViewNameTranslator(context);
        // 初始化視圖解析
        initViewResolvers(context);
        // 初始化 flashmap管理
        initFlashMapManager(context);
    }

::: warning 知識點
initStrategies方法中的所有初始化組件中之所以可以拿到值,主要是通過@EnableWebMvc註解,調用到WebMvcConfigurationSupport類中的各個@Bean註解的方法,完成的實例化過程。
:::

請求調用流程

當父子容器都啓動完成後,開始進行請求的響應處理,

  • 請求http://localhost:9090/user/queryUser地址

進入service方法

所在類:javax.servlet.http.HttpServlet

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } 
          // ...... 省略
    }

進入doGet方法

所在類:org.springframework.web.servlet.FrameworkServlet

@Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

進入processRequest方法

所在類:org.springframework.web.servlet.FrameworkServlet

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        
        doService(request, response);
}

進入doService方法

所在類:org.springframework.web.servlet.DispatcherServlet

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);
        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);
                }
            }
        }
}

請求調用 核心入口

請求最終進入doDispatch方法

所在類:org.springframework.web.servlet.DispatcherServlet

    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 {
                // 文件上傳解析,如果請求類型是multipart將通過
                // MultipartResolver進行文件上傳解析
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                // 對當前請求匹配一個合適的 handler,重要方法
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            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 {
                // 如果是multipart的請求,清空上傳的multipart資源
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

::: warning 知識點
總結,getHandler方法的主要作用體現在以下幾點:

  • 首先,從當前Request中拿到請求的URL
  • 然後,從映射關係中拿到HandlerMethod對象
  • 接着,把HandlerMethod對象封裝到HandlerExecutionChain執行鏈中
  • 最後,在HandlerExecutionChain執行鏈的創建過程中會拿到整個容器中所有的攔截器(實現HandlerInterceptor接口的攔截器),和當前請求的URL進行匹配,如果匹配成功的話,就會把攔截器放到HandlerExecutionChain的數組中。
    :::

進入getHandler方法

所在類:org.springframework.web.servlet.DispatcherServlet

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // handlerMappering 實例容器不爲空
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                // 獲取 HandlerMethod 和過濾器鏈的包裝類
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

進入getHandler方法

所在類:org.springframework.web.servlet.handler.AbstractHandlerMapping

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 根據請求的 URL 拿到對應的 HandlerMethod 對象
        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 = obtainApplicationContext().getBean(handlerName);
        }
        // 獲取 HandlerMethod 和過濾器鏈的包裝類
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        }
        else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }
        // 是否是跨域請求,就是查看 request 請求頭中是否有 Origin 屬性
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

進入getHandlerInternal方法

所在類:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 從request對象中獲取 URL,/common/query2
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();
        try {
            // 根據 URL 從映射關係中找到對應的 HandlerMethod 對象
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            // 執行beanFactory.getBean的過程,獲取Controller實例
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

::: warning 知識點
lookupHandlerMethod方法之所以可以從映射關係中拿到HandlerMethod對象,是因爲AbstractHandlerMethodMapping類實現了InitializingBean接口,在afterPropertiesSet方法裏建立好了映射關係。
:::

進入lookupHandlerMethod方法

所在類:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            // 匹配過程,是否符合 RequestMappingInfo 裏的屬性值
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                // 如果兩個 RequestMappinginfo 什麼都相同,報錯
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

::: warning 知識點
addMatchingMappings方法,主要一個匹配過程,匹配@RequestMapping註解中的屬性值是否滿足

 /*
  * consumes:指定處理請求的提交內容類型(Content-Type),
  *     例如application/json, text/html;
  * produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
  * params: 指定request中必須包含某些參數值是,才讓該方法處理。
  * headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。
  * */
@RequestMapping(value = "/getUser",
    method = RequestMethod.GET,
    params = "username=jack",
    consumes = "application/json",
    produces = "application/json",
    headers = "Referer=http://www.xx.com/")

:::

返回getHandler,進入getHandlerExecutionChain方法

所在類:org.springframework.web.servlet.handler.AbstractHandlerMapping

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        // 如果沒有獲得則創建一個 HandlerExecutionChain
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        // 獲取當前的請求地址: /user/xxx
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        // 在 HandlerExecutionChain 中添加攔截器
        // 遍歷 SpringMVC 容器的所有攔截器
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            // 判斷攔截器類型,如果是 MappedInterceptor 類型
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                // 則先匹配路徑後再添加到執行鏈
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                // 否則直接添加到執行鏈
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

::: warning 知識點
getHandlerExecutionChain中的HandlerInterceptor攔截器是Spring MVC中的,Spring AOP中的攔截器是MethodInterceptor
:::

拿到當前請求對應的handler後,

返回主流程,進入getHandlerAdapter方法

所在類:org.springframework.web.servlet.DispatcherServlet

/**
 * TODO : 根據 handlerMethod對象,找到合適的 HandlerAdapter對象,這裏用到了策略模式
 */
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    // 返回一個可以支持的HandlerAdapter 處理程序實例
                    return adapter;
                }
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

::: warning 知識點

HandlerAdapter 是什麼
HandlerAdapter是一個接口,充當自身與處理程序對象之間的橋樑,從而導致鬆散耦合設計。HandlerAdapter主要處理方法參數、相關注解、數據綁定、消息轉換、返回值、調用視圖解析器等。

  • RequestMappingHandlerMapping爲當前的請求找到合適的處理程序方法。
  • RequestMappingHandlerAdapter執行這個處理程序方法,併爲它提供反射調用所需要的參數。

HandlerAdapter UML 圖
<a data-fancybox title="採集" href="/assets/image-20200209172256575.png"><img :src="$withBase('/assets/image-20200209172256575.png')" alt=""></a>

HandlerAdapter的4個實現類:

  1. SimpleServletHandlerAdapter: 適配實現 Servlet 接口的Handler, 默認調用其service方法
  1. SimpleControllerHandlerAdapter: 適配實現Controller 接口的Handler, 默認調用其 handleRequest 方法
  2. HttpRequestHandlerAdapter: 適配實現HttpRequestHandler 接口的 Handler, 默認調用其 handleRequest 方法
  3. RequestMappingHandlerAdapter: 適配被@RequestMapping註釋的方式, 一般都是解析一個一個參數, 並且通過反射進行激活

HandlerAdapter 總結
HandlerAdapterSpring MVC中擴展機制的非常好的一個體現,,通過 HandlerAdapter這種設計模式,DispatcherServlet 就可以支持任何格式的Handler(這裏的可以支持指在不改變 DispatcherServlet 的情況下),第二是HandlerAdapter 基於不同Handler實現不同實現類(策略模式),最後也是最重要的就是參數的解析與返回值的解析。

:::

::: danger 爲什麼要用HandlerAdapter適配器模式?
首先, Controller的定義有多種 ,一種是帶@Controller註解的, 還可以寫一個servlet 當做controller, 所以用適配器做適配,不同子類實現HandlerAdapter接口,定義自己的業務邏輯,每個子類都是適配某一種類型的控制器,有了HandlerAdapter,你只需要調用自己實現的handle方法,屏蔽了不一致的細節,對用戶來說直接找到對應的處理方法,無須關係哪個實現方法,否則只能在DispatcherServlet裏面通過ifelse來處理了。
:::

前置過濾器

返回主流程,進入applyPreHandle方法,前置過濾器

所在類:org.springframework.web.servlet.DispatcherServlet

/**
 * TODO : 調用所有的 HandlerInterceptor 攔截器並調用其 preHandler方法
 */
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];
                // 分別調用攔截器的 preHandle 方法
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                // 如果失敗,記錄最後一次攔截器的位置,倒序釋放
                this.interceptorIndex = i;
            }
        }
        return true;
    }

返回主流程,進入handle方法,調用具體Controller的方法

最終會進入AbstractHandlerMethodAdapterhandle方法,

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

進入handleInternal方法,

所在類:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

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

        ModelAndView mav;
        checkRequest(request);

        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                // 執行 HandlerMethod,返回 ModelAndView
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            // 執行 HandlerMethod,返回 ModelAndView
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

進入invokeHandlerMethod方法,

所在類:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

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

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 獲取數據綁定工廠  @InitBinder註解支持,
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            // Model工廠,收集了@ModelAttribute註解的方法
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
            //可調用的方法對象
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                //設置參數解析器
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                // 設置返回值解析器
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // 設置參數綁定工廠
            invocableMethod.setDataBinderFactory(binderFactory);
            // 設置參數名稱解析類
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            // 調用有 @ModelAttribute註解的方法。每次請求都會調用有 @ModelAttribute註解的方法
            //把 @ModelAttribute註解的方法的返回值存儲到 ModelAndViewContainer對象的 map中了
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            // 異步處理
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
            // Controller方法調用,重點看看
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

::: warning 知識點
invokeHandlerMethod方法主要進行了數據和參數的綁定、創建ModelAndViewContainer視圖容器,以及相關初始化工作。
:::

進入invokeAndHandle方法

所在類:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        // 具體調用邏輯,重點看
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            // 返回值處理
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }

進入invokeForRequest方法

所在類:org.springframework.web.method.support.InvocableHandlerMethod

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        // 獲取參數數組,重點看
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        return doInvoke(args);
    }

進入getMethodArgumentValues方法

所在類:org.springframework.web.method.support.InvocableHandlerMethod

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        if (ObjectUtils.isEmpty(getMethodParameters())) {
            return EMPTY_ARGS;
        }
        // 入參的包裝類,裏面包裝了參數類型,參數名稱,參數註解等等信息
        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            // 設置參數名稱解析器
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // 典型的策略模式,根據 parameter 能否找到對應參數的處理類,能找到就返回true
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 具體參數值解析過程,重點看看
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // Leave stack trace for later, exception may actually be resolved and handled..
                if (logger.isDebugEnabled()) {
                    String error = ex.getMessage();
                    if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, error));
                    }
                }
                throw ex;
            }
        }
        return args;
    }

進入resolveArgument方法

所在類:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        // 根據參數獲取對應參數的解析類
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException(
                    "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
                            " supportsParameter should be called first.");
        }
        // 策略模式去調用具體參數解析類
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

::: warning 知識點
MethodParameter類是對參數信息的封裝,其中重要的幾個屬性包括:

  • parameterIndex:參數的索引位置
  • Parameter:具體參數的對象,包含參數名字
  • parameterType:參數的類型
  • parameterAnnotations:參數的註解數組,一個參數可以有多個註解
  • parameterName:參數名
    等等
    :::

方法參數解析

進入getArgumentResolver方法

所在類:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        // 先從緩存中拿到參數處理器對象
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        // 如果緩存中沒有
        if (result == null) {
            // 循環容器中 HandlerMethodArgumentResolver類型的所有解析器: List<HandlerMethodArgumentResolver>
            for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
                // 典型的策略模式匹配,拿到當前參數對應的處理解析類
                if (methodArgumentResolver.supportsParameter(parameter)) {
                    // 賦值給 HandlerMethodArgumentResolver 對象
                    result = methodArgumentResolver;
                    // 放到緩存中
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }

::: warning 知識點
HandlerMethodArgumentResolver參數解析器,最複雜的處理流程之一,Spring中默認有26種參數解析器,來對應完成某種參數的解析工作。添加過程是Spring MVC啓動實例化後,通過RequestMappingHandlerAdapter類的afterPropertiesSet方法調用getDefaultArgumentResolvers添加到HandlerMethodArgumentResolver解析器中的。
:::

中置過濾器

返回主流程,進入handleInternal方法,中置過濾器

所在類:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
            throws Exception {
        // 獲取所有攔截器
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                // 分別調用攔截器的 postHandle方法
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

::: warning 知識點
中置過濾器的調用時序,是當ha.handle 掉完以後,也就是Controller 裏面具體方法調用完以後才輪到中置過濾器調用。可以根據ModelAndView對象做視圖修改。
:::

後置過濾器

返回主流程,進入triggerAfterCompletion方法,後置過濾器

所在類:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
                }
            }
        }
    }

::: warning 知識點
applyPreHandle前置過濾器主要作用是對當前請求做一些初步的驗證,內部執行順序是正序遍歷, applyPostHandle 中置過濾器和triggerAfterCompletion後置過濾器的執行順序是倒序遍歷,倒序是因爲一但前置過濾器中斷(攔截器preHndle返回false)那麼請求終止,根據中斷的數組座標interceptorIndex,倒序釋放執行已經過濾的的攔截方法。
:::

到此爲止,主流程結束,以上完成了Spring MVC`從請求到處理的一系列過程,包括請求方法調用

、參數解析、過濾器調用等,接下來就是處理返回值的解析工作。

附:常見註解

::: danger 常見註解

  • @ModelAttribute:會在調用Controller的每個方法執行前被執行。

  • @RequestBody:用來處理content-typeapplication/json的類型,可以是對象。

  • @ResponseBody:一般用於返回 JSON 或 XML 數據。

  • @RequestPart:用來處理content-typemultipart/form-data類型的表單提交請求。

  • @ExceptionHandler: 用在方法上,在運行時有效,只捕獲當前Controller 中發生的異常。

  • @ControllerAdvice:用在類上,@ControllerAdvice("com.xx.xx")只對這個包裏 面的 Controller 生效,並將該類中所有使用了@ExceptionHandler 註解的方法都應用到請求處理方法上。

  • @Cacheable:若該緩存中沒有存儲該條記錄,則執行該方法,有則從緩存取。

  • @CacheEvict:將該緩存下的所有記錄都清空。

  • @CachePut:總是會執行該方法,每次都把返回結果更新進該緩存中。

  • @RequestParam:適用於所有類型的參數;

  • @RequestHeader:用於將請求頭的信息數據映射到方法參數上 。

  • @CookieValue:用於將請求的 cookie 數據映射到功能處理方法的參數上。

  • @InitBinder:用於綁定表單數據的註解。

  • @RequestAttribute:用於獲取 request作用域 中的數據。

  • @SessionAttribute:用於獲取 session作用域中的數據。

  • @PathVariable:獲取請求 URL 中的動態參數(路徑參數),如:"/test4/{id}/{name}"。

  • @MatrixVariable:擴展了URL請求地址,多個請求參數可用 分開, 一般用於進行多條件的組合查詢。

  • @CrossOrigin:用於不同域名訪問,解決跨域問題。

:::

私聊我,添加小姐姐微信,發送暗號“Spring”

即可領取全套

【Java超硬核面試資料】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章