<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring MVC請求處理過程不是兩張流程圖就能講清楚的

          共 31635字,需瀏覽 64分鐘

           ·

          2020-10-23 22:18


          來源:blog.csdn.net/Baisitao_/article/details/107471719

          前言

          SpringMVC請求處理相信大家都很熟悉了,本篇主要是基于SpringMVC處理請求的流程來閱讀并調(diào)試源碼,以及解決幾個僅靠流程圖無法解釋的問題。

          本篇使用的Spring版本為5.2.2.RELEASE

          九大組件

          SpringMVC幾乎所有的功能都由九大組件來完成,所以明白九大組件的作用,對于學(xué)習(xí)SpringMVC來說非常重要。

          /** 文件上傳解析器 */
          private?MultipartResolver multipartResolver;

          /** 區(qū)域解析器,用于國際化 */
          private?LocaleResolver localeResolver;

          /** 主題解析器 */
          private?ThemeResolver themeResolver;

          /** Handler映射信息 */
          private?List handlerMappings;

          /** Handler適配器*/
          private?List handlerAdapters;

          /** Handler執(zhí)行異常解析器 */
          private?List handlerExceptionResolvers;

          /** 請求到視圖的轉(zhuǎn)換器 */
          private?RequestToViewNameTranslator viewNameTranslator;

          /** SpringMVC允許重定向時攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap */
          private?FlashMapManager flashMapManager;

          /** 視圖解析器 */
          private?List viewResolvers;
          • HandlerMappingHandler映射信息,根據(jù)請求攜帶的url信息查找處理器(Handler)。每個請求都需要對應(yīng)的Handler處理。
          • HandlerAdapterHandler適配器,SpringMVC沒有直接調(diào)用處理器(Handler),而是通過HandlerAdapter來調(diào)用,主要是為了統(tǒng)一Handler的調(diào)用方式
          • ViewResolver:視圖解析器,用來將字符串類型的視圖名稱解析為View類型的視圖。ViewResolver需要找到渲染所用的模板和所用的技術(shù)(也就是視圖的類型)進(jìn)行渲染,具體的渲染過程則交由不同的視圖自己完成。
          • MultipartResolver:文件上傳解析器,主要用來處理文件上傳請求
          • HandlerExceptionResolver:Handler執(zhí)行異常解析器,用來對異常進(jìn)行統(tǒng)一處理
          • RequestToViewNameTranslator:請求到視圖的轉(zhuǎn)換器
          • LocaleResolver:區(qū)域解析器,用于支持國際化
          • FlashMapManagerSpringMVC允許重定向時攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap
          • ThemeResolver:主題解析器,用于支持不同的主題
          九大組件中最重的的前三個,HandlerMappingHandlerAdapterViewResolver,因為這是閱讀源碼時,避不開的三個組件。

          調(diào)試準(zhǔn)備

          搭建一個基本的Spring web項目即可
          Controller部分
          @Controller
          public?class?IndexController {

          ????@RequestMapping("/index/home")
          ????public?String?home(String?id, Student student, @RequestParam("code") String?code) {
          ????????System.out.println(student.getName());
          ????????return?"index";
          ????}

          ????@ResponseBody
          ????@RequestMapping("/index/list")
          ????public?String?list() {
          ????????return?"success";
          ????}
          }


          Entity部分

          public?class?Student?{

          ????private?String name;
          ????private?Integer gender;

          ???// getter、setter
          }
          還是那句話,Spring源碼非常龐大,不能只見樹木不見森林,需要有針對性的閱讀,所以本篇只需要關(guān)注主體流程即可。

          核心方法

          我們都知道,SpringMVC有一個用來分發(fā)請求的前端控制器DispatcherServlet,其中用來處理請求的方法就是doService,該方法定義如下
          doService
          /**
          ?* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
          ?* for the actual dispatching.
          ?*/

          @Override
          protected?void?doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
          ?logRequest(request);

          ?// Keep a snapshot of the request attributes in case of an include,
          ?// to be able to restore the original attributes after the include.
          ?Map<String, Object> attributesSnapshot = null;
          ?if?(WebUtils.isIncludeRequest(request)) {
          ??attributesSnapshot = new?HashMap<>();
          ??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));
          ???}
          ??}
          ?}

          ?// Make framework objects available to handlers and view objects.
          ?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());

          ?if?(this.flashMapManager != null) {
          ??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?{
          ??// 真正執(zhí)行的方法
          ??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

          doDispatchdoService中真正用來處理請求的方法
          /**
          ?* 實際處理請求的方法
          ?*/

          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);

          ???// Determine handler for the current request.
          ???// 為當(dāng)前請求找到一個合適的處理器(Handler)
          ???// 返回值是一個HandlerExecutionChain,也就是處理器執(zhí)行鏈
          ???mappedHandler = getHandler(processedRequest);
          ???if?(mappedHandler == null) {
          ????noHandlerFound(processedRequest, response);
          ????return;
          ???}

          ???// Determine handler adapter for the current request.
          ???// 根據(jù)HandlerExecutionChain攜帶的Handler找到合適的HandlerAdapter
          ???HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

          ???// Process last-modified header, if supported by the handler.
          ???// 處理GET請求的緩存
          ???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;
          ????}
          ???}

          ???// 執(zhí)行攔截器的preHandle方法
          ???if?(!mappedHandler.applyPreHandle(processedRequest, response)) {
          ????return;
          ???}

          ???// Actually invoke the handler.
          ???// 利用HandlerAdapter來執(zhí)行Handler里對應(yīng)的處理方法
          ???mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

          ???// 如果沒有設(shè)置視圖,則應(yīng)用默認(rèn)的視圖名
          ???applyDefaultViewName(processedRequest, mv);
          ???// 執(zhí)行攔截器的postHandle方法
          ???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);
          ??}
          ??// 根據(jù)ModelAndView對象解析視圖
          ??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);
          ???}
          ??}
          ?}
          }


          該方法就是SpringMVC處理請求的整體流程,其中涉及到幾個重要的方法。

          getHandler

          該方法定義如下
          /**
          ?* Return the HandlerExecutionChain for this request.
          ?* 為這個request返回一個HandlerExecutionChain
          ?*/

          @Nullable
          protected?HandlerExecutionChain getHandler(HttpServletRequest request)?throws?Exception {
          ?if?(this.handlerMappings != null) {
          ??for?(HandlerMapping mapping : this.handlerMappings) {
          ???HandlerExecutionChain handler = mapping.getHandler(request);
          ???if?(handler != null) {
          ????return?handler;
          ???}
          ??}
          ?}
          ?return?null;
          }


          調(diào)試信息如下

          根據(jù)調(diào)試信息可以看出,getHandler方法主要是從List handlerMappings集合中遍歷查找一個合適的處理器(Handler),返回的結(jié)果是一個HandlerExecutionChain。然后再根據(jù)HandlerExecutionChain里攜帶的Handler去獲取HandlerAdapter

          getHandlerAdapter

          getHandlerAdapter方法定義如下
          /**
          ??* Return the HandlerAdapter for this handler object.
          ??* @param?handler the handler object to find an adapter for
          ??* @throws?ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
          ??*/

          ?protected?HandlerAdapter getHandlerAdapter(Object handler)?throws?ServletException {
          ??if?(this.handlerAdapters != null) {
          ???for?(HandlerAdapter adapter : this.handlerAdapters) {
          ????if?(adapter.supports(handler)) {
          ?????return?adapter;
          ????}
          ???}
          ??}
          ??throw?new?ServletException("No adapter for handler ["?+ handler +
          ????"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
          ?}
          調(diào)試信息如下


          同樣getHandlerAdapter方法主要是從List handlerAdapters集合中遍歷查找一個合適的處理器適配器(HandlerAdapter),返回的結(jié)果是一個HandlerAdapter
          可以看到此處HandlerAdapter真正的實現(xiàn)類是RequestMappingHandlerAdapter

          processDispatchResult

          processDispatchResult方法主要根據(jù)方法執(zhí)行完成后封裝的ModelAndView,轉(zhuǎn)發(fā)到對應(yīng)頁面,定義如下
          /**
          ?* Handle the result of handler selection and handler invocation, which is
          ?* either a ModelAndView or an Exception to be resolved to a ModelAndView.
          ?*/

          private?void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
          ??@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
          ??@Nullable Exception?exception) throws Exception?{

          ?boolean errorView = false;

          ?if?(exception?!= null) {
          ??if?(exception?instanceof?ModelAndViewDefiningException) {
          ???logger.debug("ModelAndViewDefiningException encountered", exception);
          ???mv = ((ModelAndViewDefiningException) exception).getModelAndView();
          ??}
          ??else?{
          ???Object handler = (mappedHandler != null?? mappedHandler.getHandler() : null);
          ???mv = processHandlerException(request, response, handler, exception);
          ???errorView = (mv != null);
          ??}
          ?}

          ?// Did the handler return a view to render?
          ?if?(mv != null?&& !mv.wasCleared()) {
          ??// 主要調(diào)用該方法渲染視圖
          ??render(mv, request, response);
          ??if?(errorView) {
          ???WebUtils.clearErrorRequestAttributes(request);
          ??}
          ?}
          ?else?{
          ??if?(logger.isTraceEnabled()) {
          ???logger.trace("No view rendering, null ModelAndView returned.");
          ??}
          ?}

          ?if?(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
          ??// Concurrent handling started during a forward
          ??return;
          ?}

          ?if?(mappedHandler != null) {
          ??// Exception (if any) is already handled..
          ??mappedHandler.triggerAfterCompletion(request, response, null);
          ?}
          }

          render

          render方法定義如下
          /**
          ?* Render the given ModelAndView.
          ?*

          This is the last stage in handling a request. It may involve resolving the view by name.
          ?* @param?mv the ModelAndView to render
          ?* @param?request current HTTP servlet request
          ?* @param?response current HTTP servlet response
          ?* @throws?ServletException if view is missing or cannot be resolved
          ?* @throws?Exception if there's a problem rendering the view
          ?*/
          protected?void?render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)?throws?Exception {
          ?// Determine locale for request and apply it to the response.
          ?Locale locale =
          ???(this.localeResolver != null?? this.localeResolver.resolveLocale(request) : request.getLocale());
          ?response.setLocale(locale);

          ?View view;
          ?String viewName = mv.getViewName();
          ?if?(viewName != null) {
          ??// We need to resolve the view name.
          ??// 根據(jù)給定的視圖名稱,解析獲取View對象
          ??view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
          ??if?(view == null) {
          ???throw?new?ServletException("Could not resolve view with name '"?+ mv.getViewName() +
          ?????"' in servlet with name '"?+ getServletName() + "'");
          ??}
          ?}
          ?else?{
          ??// No need to lookup: the ModelAndView object contains the actual View object.
          ??view = mv.getView();
          ??if?(view == null) {
          ???throw?new?ServletException("ModelAndView ["?+ mv + "] neither contains a view name nor a "?+
          ?????"View object in servlet with name '"?+ getServletName() + "'");
          ??}
          ?}

          ?// Delegate to the View object for rendering.
          ?if?(logger.isTraceEnabled()) {
          ??logger.trace("Rendering view ["?+ view + "] ");
          ?}
          ?try?{
          ??if?(mv.getStatus() != null) {
          ???response.setStatus(mv.getStatus().value());
          ??}
          ??view.render(mv.getModelInternal(), request, response);
          ?}
          ?catch?(Exception ex) {
          ??if?(logger.isDebugEnabled()) {
          ???logger.debug("Error rendering view ["?+ view + "]", ex);
          ??}
          ??throw?ex;
          ?}
          }

          resolveViewName

          resolveViewName方法定義如下

          @Nullable
          protected?View resolveViewName(String?viewName, @Nullable?Map<String, Object> model,
          ??Locale locale, HttpServletRequest request) throws Exception {

          ?if?(this.viewResolvers != null) {
          ??for?(ViewResolver viewResolver : this.viewResolvers) {
          ???View view = viewResolver.resolveViewName(viewName, locale);
          ???if?(view != null) {
          ????return?view;
          ???}
          ??}
          ?}
          ?return?null;
          }
          調(diào)試信息如下

          根據(jù)調(diào)試信息可以看到真正解析視圖的ViewResolver的是InternalResourceViewResolver類,也就是我們經(jīng)常配置的一項類型

          <bean?class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          ?<property?name="prefix"?value="/WEB-INF/views/"?/>
          ?<property?name="suffix"?value=".html"?/>
          bean>
          至此我們就得到了SpringMVC處理請求的完整邏輯

          SpringMVC處理請求的整個流程已經(jīng)梳理清楚了。
          但是,有兩個重要的問題沒有解決,那就是:參數(shù)綁定和返回值處理。
          > 因為在編寫Controller里面的方法的時候,各種類型的參數(shù)都有,SpringMVC是怎么處理不同類型的參數(shù)的呢?
          > SpringMVC處理請求完成后,一定會返回
          ModelAndView嗎,如果加了@ResponseBody注解呢?

          參數(shù)綁定

          在整個流程中,還有一個最重要的方法,那就是真正執(zhí)行handler的方法,參數(shù)的綁定和返回值的處理都在這個方法里,也就是
          // Actually invoke the handler.
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

          handle

          handle方法的作用是根據(jù)請求參數(shù),執(zhí)行真正的處理方法,并且返回合適的ModelAndView對象,也有可能返回null。該方法定義如下
          AbstractHandlerMethodAdapter類中
          /**
          ?* This implementation expects the handler to be an {@link HandlerMethod}.
          ?*/

          @Override
          @Nullable
          public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
          ??throws Exception {

          ?return?handleInternal(request, response, (HandlerMethod) handler);
          }
          可以看到這個方法實現(xiàn)只有一行代碼

          handleInternal

          繼續(xù)深入handleInternal方法
          @Override
          protected?ModelAndView handleInternal(HttpServletRequest request,
          ??HttpServletResponse response, HandlerMethod handlerMethod)
          ?throws?Exception
          {

          ?ModelAndView mav;
          ?// 校驗指定的請求以獲取受支持的方法類型(GET、POST等)和所需的session
          ?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
          ???mav = invokeHandlerMethod(request, response, handlerMethod);
          ??}
          ?}
          ?else?{
          ??// No synchronization on session demanded at all...
          ??// 真正執(zhí)行handler的方法
          ??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

          繼續(xù)深入invokeHandlerMethod方法
          /**
          ?* Invoke the {@link?RequestMapping} handler method preparing a {@link?ModelAndView}
          ?* if view resolution is required.
          ?* 執(zhí)行@RequestMapping標(biāo)注的handler方法,如果需要解析視圖就準(zhǔn)備一個ModelAndView
          ?*/

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

          ?ServletWebRequest webRequest = new?ServletWebRequest(request, response);
          ?try?{
          ??WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
          ??ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

          ??// HandlerMethod接口封裝執(zhí)行方法的信息,提供對方法參數(shù),方法返回值,方法注釋等的便捷訪問。
          ??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可以看做ModelAndView的上下文容器,關(guān)聯(lián)著Model和View的信息
          ??ModelAndViewContainer mavContainer = new?ModelAndViewContainer();
          ??mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
          ??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);
          ??}

          ??// 真正執(zhí)行Handler的方法
          ??invocableMethod.invokeAndHandle(webRequest, mavContainer);
          ??if?(asyncManager.isConcurrentHandlingStarted()) {
          ???return?null;
          ??}

          ??// 獲取ModelAndeView對象
          ??return?getModelAndView(mavContainer, modelFactory, webRequest);
          ?}
          ?finally?{
          ??webRequest.requestCompleted();
          ?}
          }
          invokeAndHandle
          invokeAndHandle方法的作用是執(zhí)行并處理真正響應(yīng)請求的方法,該方法定義如下
          /**
          ?* Invoke the method and handle the return value through one of the
          ?* configured {@link?HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
          ?* @param?webRequest the current request
          ?* @param?mavContainer the ModelAndViewContainer for this request
          ?* @param?providedArgs "given" arguments matched by type (not resolved)
          ?*/

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

          ?// 執(zhí)行handler的方法
          ?Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
          ?setResponseStatus(webRequest);

          ?if?(returnValue == null) {
          ??if?(isRequestNotModified(webRequest) || getResponseStatus() != null?|| mavContainer.isRequestHandled()) {
          ???disableContentCachingIfNecessary(webRequest);
          ???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

          /**
          ?* Invoke the method after resolving its argument values in the context of the given request.
          ?*

          Argument values are commonly resolved through
          ?* {@link?HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
          ?* The {@code?providedArgs} parameter however may supply argument values to be used directly,
          ?* i.e. without argument resolution. Examples of provided argument values include a
          ?* {@link?WebDataBinder}, a {@link?SessionStatus}, or a thrown exception instance.
          ?* Provided argument values are checked before argument resolvers.
          ?*

          Delegates to {@link?#getMethodArgumentValues} and calls {@link?#doInvoke} with the
          ?* resolved arguments.
          ?* @param?request the current request
          ?* @param?mavContainer the ModelAndViewContainer for this request
          ?* @param?providedArgs "given" arguments matched by type, not resolved
          ?* @return?the raw value returned by the invoked method
          ?* @throws?Exception raised if no suitable argument resolver can be found,
          ?* or if the method raised an exception
          ?* @see?#getMethodArgumentValues
          ?* @see?#doInvoke
          ?*/
          @Nullable
          public?Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
          ??Object... providedArgs) throws Exception?{

          ?// 獲取參數(shù)
          ?Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
          ?if?(logger.isTraceEnabled()) {
          ??logger.trace("Arguments: "?+ Arrays.toString(args));
          ?}
          ?// 執(zhí)行
          ?return?doInvoke(args);
          }


          真正的執(zhí)行無非就是通過反射invoke,所以更重要的是參數(shù)是如何綁定的,詳情就在getMethodArgumentValues方法

          getMethodArgumentValues

          getMethodArgumentValues方法用于從request請求中獲取真正的參數(shù),返回的是Object數(shù)組,該方法定義如下
          /**
          ?* Get the method argument values for the current request, checking the provided
          ?* argument values and falling back to the configured argument resolvers.
          ?*

          The resulting array will be passed into {@link #doInvoke}.
          ?* @since 5.1.2
          ?*/
          protected?Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable?ModelAndViewContainer mavContainer,
          ??Object... providedArgs) throws Exception {

          ?// 獲取方法上所有的參數(shù)
          ?MethodParameter[] parameters = getMethodParameters();
          ?if?(ObjectUtils.isEmpty(parameters)) {
          ??return?EMPTY_ARGS;
          ?}

          ?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;
          ??}
          ??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?exMsg = ex.getMessage();
          ????if?(exMsg != null?&& !exMsg.contains(parameter.getExecutable().toGenericString())) {
          ?????logger.debug(formatArgumentError(parameter, exMsg));
          ????}
          ???}
          ???throw?ex;
          ??}
          ?}
          ?return?args;
          }



          根據(jù)調(diào)試信息可以看到,用來處理請求參數(shù)的類是HandlerMethodArgumentResolver接口的實現(xiàn)類HandlerMethodArgumentResolverComposite,此時正在處理的參數(shù)是一個Student對象,并且已經(jīng)把值注綁定了,也就是說真正執(zhí)行綁定的是方法resolveArgument

          resolveArgument

          resolveArgument是真正執(zhí)行綁定的的方法
          /**
          ?* Iterate over registered
          ?* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
          ?* and invoke the one that supports it.
          ?* @throws IllegalArgumentException if no suitable argument resolver is found
          ?*/

          @Override
          @Nullable
          public?Object?resolveArgument(MethodParameter parameter, @Nullable?ModelAndViewContainer mavContainer,
          ??NativeWebRequest webRequest, @Nullable?WebDataBinderFactory binderFactory) throws Exception {

          ?// 獲取合適的參數(shù)解析器
          ?HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
          ?if?(resolver == null) {
          ??throw?new?IllegalArgumentException("Unsupported parameter type ["?+
          ????parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
          ?}
          ?// 執(zhí)行參數(shù)綁定
          ?return?resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
          }

          getArgumentResolver

          getArgumentResolver該方法用于執(zhí)行參數(shù)的綁定,定義如下
          /**
          ?* Find a registered {@link HandlerMethodArgumentResolver} that supports
          ?* the given method parameter.
          ?*/

          @Nullable
          private?HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter)
          {
          ?HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
          ?if?(result == null) {
          ??for?(HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
          ???if?(resolver.supportsParameter(parameter)) {
          ????result = resolver;
          ????this.argumentResolverCache.put(parameter, result);
          ????break;
          ???}
          ??}
          ?}
          ?return?result;
          }


          該方法的邏輯就是先從argumentResolver緩存中找到能夠執(zhí)行參數(shù)綁定的HandlerMethodArgumentResolver,如果找不到就從HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26種,用來解析各種類型的參數(shù)


          根據(jù)博主的調(diào)試可以知道
          • RequestParamMethodArgumentResolver:處理普通參數(shù)(基本類型、包裝類型、String),不管加不加@RequestParam注解

          • ServletModelAttributeMethodProcessor:處理POJO類型的參數(shù),比如自定義的Student對象

          • RequestResponseBodyMethodProcessor:處理@RequestBody注解類型的參數(shù)

          resolveArgument

          由于不同類型的參數(shù)有不同的HandlerMethodArgumentResolver來處理,此處選取POJO類型參數(shù)的注入實現(xiàn),對應(yīng)的參數(shù)解析類是ModelAttributeMethodProcessor,其中resolveArgument方法用來解析(綁定)參數(shù)方法定義如下
          /**
          ?* Resolve the argument from the model or if not found instantiate it with
          ?* its default if it is available. The model attribute is then populated
          ?* with request values via data binding and optionally validated
          ?* if {@code @java.validation.Valid} is present on the argument.
          ?* @throws BindException if data binding and validation result in an error
          ?* and the next method parameter is not of type {@link Errors}
          ?* @throws Exception if WebDataBinder initialization fails
          ?*/

          @Override
          @Nullable
          public?final Object?resolveArgument(MethodParameter parameter, @Nullable?ModelAndViewContainer mavContainer,
          ??NativeWebRequest webRequest, @Nullable?WebDataBinderFactory binderFactory) throws Exception {

          ?Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
          ?Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

          ?// 獲取參數(shù)名
          ?String?name = ModelFactory.getNameForParameter(parameter);
          ?// 獲取參數(shù)上的ModelAttribute注解
          ?ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
          ?if?(ann != null) {
          ??mavContainer.setBinding(name, ann.binding());
          ?}

          ?Object?attribute = null;
          ?BindingResult bindingResult = null;

          ?if?(mavContainer.containsAttribute(name)) {
          ??attribute = mavContainer.getModel().get(name);
          ?}
          ?else?{
          ??// Create attribute instance
          ??try?{
          ???// 創(chuàng)建參數(shù)類型的實例(未注入值),底層就是通過反射調(diào)用構(gòu)造方法
          ???attribute = createAttribute(name, parameter, binderFactory, webRequest);
          ??}
          ??catch?(BindException ex) {
          ???if?(isBindExceptionRequired(parameter)) {
          ????// No BindingResult parameter -> fail with BindException
          ????throw?ex;
          ???}
          ???// Otherwise, expose null/empty value and associated BindingResult
          ???if?(parameter.getParameterType() == Optional.class) {
          ????attribute = Optional.empty();
          ???}
          ???bindingResult = ex.getBindingResult();
          ??}
          ?}

          ?if?(bindingResult == null) {
          ??// Bean property binding and validation;
          ??// skipped in case of binding failure on construction.
          ??WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
          ??if?(binder.getTarget() != null) {
          ???if?(!mavContainer.isBindingDisabled(name)) {
          ????// 真正執(zhí)行綁定(值注入)的方法
          ????bindRequestParameters(binder, webRequest);
          ???}
          ???validateIfApplicable(binder, parameter);
          ???if?(binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
          ????throw?new?BindException(binder.getBindingResult());
          ???}
          ??}
          ??// Value type adaptation, also covering java.util.Optional
          ??if?(!parameter.getParameterType().isInstance(attribute)) {
          ???attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
          ??}
          ??bindingResult = binder.getBindingResult();
          ?}

          ?// Add resolved attribute and BindingResult at the end of the model
          ?Map<String, Object> bindingResultModel = bindingResult.getModel();
          ?mavContainer.removeAttributes(bindingResultModel);
          ?mavContainer.addAllAttributes(bindingResultModel);

          ?return?attribute;
          }



          根據(jù)調(diào)試信息也可以看到bindRequestParameters(binder, webRequest)執(zhí)行完成之后,POJO類型的參數(shù)已經(jīng)完成了綁定。

          bindRequestParameters

          /**
          ?* This implementation downcasts {@link?WebDataBinder} to
          ?* {@link?ServletRequestDataBinder} before binding.
          ?* @see?ServletRequestDataBinderFactory
          ?*/

          @Override
          protected?void?bindRequestParameters(WebDataBinder binder, NativeWebRequest request)?{
          ?ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
          ?Assert.state(servletRequest != null, "No ServletRequest");
          ?ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
          ?// 執(zhí)行綁定的方法
          ?servletBinder.bind(servletRequest);
          }

          bind

          繼續(xù)深入bind方法
          public?void?bind(ServletRequest request) {
          ?// 獲取所有參數(shù)的鍵值對
          ?MutablePropertyValues mpvs = new?ServletRequestParameterPropertyValues(request);
          ?// 處理文件上傳請求
          ?MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
          ?if?(multipartRequest != null) {
          ??bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
          ?}
          ?// 把url中攜帶的參數(shù)也加入到MutablePropertyValues
          ?addBindValues(mpvs, request);
          ?// 執(zhí)行綁定(注入值)
          ?doBind(mpvs);
          }


          由于調(diào)用層次過深,所以無法一步步列出下面的步驟,doBind方法的原理還是通過調(diào)用POJO對象里的setter方法設(shè)置值,可以查看最終的調(diào)試信息


          根據(jù)調(diào)試信息可以看到,最終執(zhí)行的還是POJO對象的setter方法,具體執(zhí)行的類是BeanWrapperImpl
          了解了參數(shù)的綁定,再來看返回值的處理。

          返回值處理

          invokeAndHandle

          回到源碼invokeAndHandle方法處(ServletInvocableHandlerMethod類中),該方法定義如下
          /**
          ?* Invoke the method and handle the return value through one of the
          ?* configured {@link?HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
          ?* @param?webRequest the current request
          ?* @param?mavContainer the ModelAndViewContainer for this request
          ?* @param?providedArgs "given" arguments matched by type (not resolved)
          ?*/

          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()) {
          ???disableContentCachingIfNecessary(webRequest);
          ???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;
          ?}
          }
          真正處理不同類型的返回值的方法是handleReturnValue方法

          handleReturnValue

          /**
          ?* Iterate over registered {@link?HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
          ?* @throws?IllegalStateException if no suitable {@link?HandlerMethodReturnValueHandler} is found.
          ?*/

          @Override
          public?void?handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
          ??ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
          ?throws?Exception
          {

          ?// 根據(jù)返回值個返回值類型選取合適的HandlerMethodReturnValueHandler
          ?HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
          ?if?(handler == null) {
          ??throw?new?IllegalArgumentException("Unknown return value type: "?+ returnType.getParameterType().getName());
          ?}
          ?// 真正的處理返回值
          ?handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
          }

          selectHandler

          @Nullable
          private?HandlerMethodReturnValueHandler selectHandler(@Nullable?Object?value, MethodParameter returnType) {
          ?boolean?isAsyncValue = isAsyncReturnValue(value, returnType);
          ?for?(HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
          ??if?(isAsyncValue && !(handler instanceof?AsyncHandlerMethodReturnValueHandler)) {
          ???continue;
          ??}
          ??if?(handler.supportsReturnType(returnType)) {
          ???return?handler;
          ??}
          ?}
          ?return?null;
          }



          根據(jù)調(diào)試信息可以看到,SpringMVC為返回值提供了15個HandlerMethodReturnValueHandler的實現(xiàn)了來處理不同類型的返回值。
          事實上,用來處理@ResponseBody類型的是RequestResponseBodyMethodProcessor
          如果對前文參數(shù)綁定還有印象的話,會發(fā)現(xiàn)@RequestBody類型參數(shù)綁定也是用的這個類。
          繼續(xù)跟進(jìn)RequestResponseBodyMethodProcessor類的handleReturnValue方法

          handleReturnValue

          RequestResponseBodyMethodProcessor類的handleReturnValue方法定義如下
          這里設(shè)置了一個非常重要的屬性requestHandled,這個屬性關(guān)系到是否需要返回ModelAndView對象
          @Override
          public?void?handleReturnValue(@Nullable?Object?returnValue, MethodParameter returnType,
          ??ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
          ??throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

          ?// 設(shè)置該請求是否已在處理程序中完全處理,例如@ResponseBody方法不需要視圖解析器,此處就可以設(shè)置為true。
          ?// 當(dāng)控制器方法聲明類型為ServletResponse或OutputStream的參數(shù)時,也可以設(shè)置此標(biāo)志為true。
          ?// 這個屬性設(shè)置成true之后,上層getModelAndView獲取ModelAndView時會返回Null,因為不需要視圖。
          ?// 默認(rèn)值為false
          ?mavContainer.setRequestHandled(true);
          ?ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
          ?ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

          ?// Try even with null return value. ResponseBodyAdvice could get involved.
          ?// 底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO
          ?writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
          }
          繼續(xù)深入writeWithMessageConverters方法,一步步調(diào)試到最后,底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO


          由于handleReturnValuerequestHandled設(shè)置成了true,上層在調(diào)用getModelAndView方法時會返回null,表示該請求不需要視圖。感興趣的同學(xué)自己調(diào)試一下便知。

          總結(jié)

          本文主要從源碼的閱讀和調(diào)試的角度,整體的講解了SpringMVC處理請求的整個流程,并且講解了參數(shù)的綁定以及返回值的處理。相信大家看完后,結(jié)合自己的調(diào)試信息,會對SpringMVC的請求處理過程有一個更深入的理解。

          后臺回復(fù)?學(xué)習(xí)資料?領(lǐng)取學(xué)習(xí)視頻


          如有收獲,點個在看,誠摯感謝


          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美精品一二三 | 黄色免费高清视频 | 国产精品嫩草AV城中村 | 亚洲黄色影视 | 久热免费在线视频 |