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的近乎唯一选择,所以它成为了实际意义上的标准实现,深入了解掌握它其实非常有必要。

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


相关文章
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
52 4
|
2月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
137 3
|
2月前
|
运维 负载均衡 安全
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
110 0
|
28天前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
50 5
|
29天前
|
JSON JavaScript 前端开发
蓝桥杯web组赛题解析和杯赛技巧
本文作者是一位自学前端两年半的大一学生,在第十五届蓝桥杯Web组比赛中获得省一和国三。文章详细解析了比赛题纲,涵盖HTML、CSS、JavaScript、Echarts和Vue等技术要点,并分享了备赛技巧和比赛经验。作者强调了多写代码和解题思路的重要性,同时提供了省赛和国赛的具体流程及注意事项。希望对参赛者有所帮助。
59 3
|
1月前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
83 4
|
1月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
66 2
|
2月前
|
前端开发 开发者 容器
构建响应式Web界面:Flexbox与Grid布局的深度解析
【10月更文挑战第11天】本文深入解析了CSS3中的Flexbox和Grid布局,探讨了它们的特点、应用场景及使用方法。Flexbox适用于一维布局,如导航栏;Grid布局则适用于二维布局,如复杂网格。通过示例代码和核心属性介绍,帮助开发者灵活构建响应式Web界面。
57 5
|
2月前
|
前端开发 JavaScript 开发者
Web组件:一种新的前端开发范式
【10月更文挑战第9天】Web组件:一种新的前端开发范式
55 2
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。

推荐镜像

更多