web九大组件之---RequestMappingHandlerAdapter详尽解析【享学Spring MVC】(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: web九大组件之---RequestMappingHandlerAdapter详尽解析【享学Spring MVC】(下)

方法:

RequestMappingHandlerAdapter:
  ... // 省略所有属性的get/set方法
  @Override
  protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
    return -1;
  }
  // 因为它只需要处理HandlerMethod这样的Handler,所以这里恒返回true  请参照父类的supportsInternal()钩子方法
  @Override
  protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
  }
  // 可以认为这个就是`HandlerAdapter`的接口方法,是处理请求的入口 最终返回一个ModelAndView
  @Override
  protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    checkRequest(request); // 检查方法
    // Execute invokeHandlerMethod in synchronized block if required.
    // 同一个Session下是否要串行,显然一般都是不需要的。直接看invokeHandlerMethod()方法吧
    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...
      mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    // 处理Cache-Control这个请求头~~~~~~~~~(若你自己木有set的话)
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      } else {
        prepareResponse(response);
      }
    }
    return mav;
  }


剩下的便是本适配器最为重要的一个方法:invokeHandlerMethod()


invokeHandlerMethod()


它的作用顾名思义:就是调用我们的目标方法,在这里会组织各个组件进行数据绑定、数据校验、内容协商等等操作控制。


  // 它的作用就是执行目标的HandlerMethod,然后返回一个ModelAndView 
  @Nullable
  protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // 注意:此处只有try-finally 哦
    // 因为invocableMethod.invokeAndHandle(webRequest, mavContainer)是可能会抛出异常的(交给全局异常处理)
    try {
      // 最终创建的是一个ServletRequestDataBinderFactory,持有所有@InitBinder的method方法们
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      // 创建一个ModelFactory,@ModelAttribute啥的方法就会被引用进来
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
      // 把HandlerMethod包装为ServletInvocableHandlerMethod,具有invoke执行的能力喽
      // 下面这几部便是一直给invocableMethod的各大属性赋值~~~
      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();
      // 把上个request里的值放进来到本request里
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      // model工厂:把它里面的Model值放进mavContainer容器内(此处@ModelAttribute/@SessionAttribute啥的生效)
      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);
      // 它不管是不是异步请求都先用AsyncWebRequest 包装了一下,但是若是同步请求
      // asyncManager.hasConcurrentResult()肯定是为false的~~~
      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);
      }
      // 此处其实就是调用ServletInvocableHandlerMethod#invokeAndHandle()方法喽
      // 关于它你可以来这里:https://fangshixiang.blog.csdn.net/article/details/98385163
      // 注意哦:任何HandlerMethod执行完后都是把结果放在了mavContainer里(它可能有Model,可能有View,可能啥都木有~~)
      // 因此最后的getModelAndView()又得一看
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
        return null;
      }
      return getModelAndView(mavContainer, modelFactory, webRequest);
    } finally {
      webRequest.requestCompleted();
    }
  }


可以看到任何处理器执行完后,最终返回的的都是一个ModelAndView对象,保持了很好的同一性。这得益于getModelAndView()这个方法的适配处理:

  // @Nullable:表示它返回的可以是个null哦~(若木有视图,就直接不会render啦~因为response已经写入过值了)
  @Nullable
  private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    // 把session里面的内容写入
    modelFactory.updateModel(webRequest, mavContainer);
    // Tips:若已经被处理过,那就返回null喽~~(比如若是@ResponseBody这种,这里就是true)
    if (mavContainer.isRequestHandled()) {
      return null;
    }
    // 通过View、Model、Status构造出一个ModelAndView,最终就可以完成渲染了
    ModelMap model = mavContainer.getModel();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) { // 是否是String类型
      mav.setView((View) mavContainer.getView());
    }
    // 对重定向RedirectAttributes参数的支持(两个请求之间传递参数,使用的是ATTRIBUTE)
    if (model instanceof RedirectAttributes) {
      Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
      if (request != null) {
        RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
    }
    return mav;
  }


执行完HandlerMethod后得到一个ModelAndView,它可能是null(比如已被处理过),那么最终交给DispatcherServlet就没有后续处理(渲染)了,否则会做视图渲染:render()。


关于DispatcherServlet的处理流程,站内搜索相关关键字也能找到相关文章的。


Spring MVC默认装配了哪些HandlerAdapter呢?


开启@EnableWebMvc:

image.png

不开启:image.png


相关阅读

web九大组件之—HandlerAdapter适配器模式实践源码分析【享学Spring MVC】


总结


RequestMappingHandlerAdapter作为HandlerAdapter适配模式实现,由于@RequestMapping成为了使用Spring MVC的近乎唯一选择,所以它成为了实际意义上的标准实现,深入了解掌握它其实非常有必要。

本类具有内容多、体系庞大、实现复杂等特点,源码啃起来相较于其它模块还是要更加费劲的,这主要是由于它"集成"进来的组件非常之多,而每一个基础组件也是一个重要的且较难理解的知识点,因此路漫漫其修远兮…多看几遍吧,说不定啥时候就豁然开朗了。


相关文章
|
14天前
|
前端开发 JavaScript
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
54 17
|
25天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
46 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
20天前
|
前端开发 UED
React 文本区域组件 Textarea:深入解析与优化
本文介绍了 React 中 Textarea 组件的基础用法、常见问题及优化方法,包括状态绑定、初始值设置、样式自定义、性能优化和跨浏览器兼容性处理,并提供了代码案例。
48 8
|
27天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
102 2
|
27天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
46 2
|
1月前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
89 5
|
27天前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
68 0
|
1月前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
44 1
|
2月前
|
存储 JavaScript 前端开发
Vue3权限控制全攻略:路由与组件层面的用户角色与权限管理方法深度解析
Vue3权限控制全攻略:路由与组件层面的用户角色与权限管理方法深度解析
235 2
|
1月前
|
机器学习/深度学习 自然语言处理 数据管理
GraphRAG核心组件解析:图结构与检索增强生成
【10月更文挑战第28天】在当今数据科学领域,自然语言处理(NLP)和图数据管理技术的发展日新月异。GraphRAG(Graph Retrieval-Augmented Generation)作为一种结合了图结构和检索增强生成的创新方法,已经在多个应用场景中展现出巨大的潜力。作为一名数据科学家,我对GraphRAG的核心组件进行了深入研究,并在此分享我的理解和实践经验。
73 0

推荐镜像

更多