教你SpringMVC处理请求的方式

本文详细介绍了SpringMVC处理请求的流程,从用户发送请求到DispatcherServlet,经过HandlerMapping,HandlerAdapter,再到Controller执行,最后返回ModelAndView并渲染视图。文中还涉及到源码分析,帮助读者深入理解SpringMVC的工作原理。
摘要由CSDN通过智能技术生成

很多人会用 SpringMVC,但对它的处理请求的方式并不清楚,当我们学习一个知识的时候,了解它会让我们更好地使用它,下面我们来看看 SpringMVC 是如何处理请求的。

请求流程的方式

先上图:

Spring MVC 框架也是一个基于请求驱动的 Web 框架,并且使用了前端控制器模式(是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理来进行设计,再根据请求映射规则分发给相应的页面控制器(动作/处理器)进行处理。首先让我们整体看一下 Spring MVC 处理请求的流程:

  1. 首先用户发送请求,请求被 SpringMVC前端控制器(DispatherServlet)捕获;

  2. 前端控制器(DispatherServlet)对请求 URL 解析获取请求 URI,根据 URI,调用 HandlerMapping;

  3. 前端控制器(DispatherServlet)获得返回的 HandlerExecutionChain(包括 Handler 对象以及 Handler 对象对应的拦截器);

  4. DispatcherServlet 根据获得的 HandlerExecutionChain,选择一个合适的 HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(…) 方法);

  5. HandlerAdapter 根据请求的 Handler 适配并执行对应的 Handler;HandlerAdapter 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充 Handler 的入参过程中,根据配置,Spring 将做一些额外的工作:

    HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息;

    数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等;

    数据格式化:如将字符串转换成格式化数字或格式化日期等;

    数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中);

  6. Handler 执行完毕,返回一个 ModelAndView (即模型和视图)给 HandlerAdaptor;

  7. HandlerAdaptor 适配器将执行结果 ModelAndView 返回给前端控制器;

  8. 前端控制器接收到 ModelAndView 后,请求对应的视图解析器;

  9. 视图解析器解析 ModelAndView 后返回对应 View;

  10. 渲染视图并返回渲染后的视图给前端控制器;

  11. 最终前端控制器将渲染后的页面响应给用户或客户端。

案例实操

SpringMVC 请求执行源码解读

对于 SpringMVC 项目所有的请求入口(静态资源除外)这里都是从 web.xml 文件配置的前端控制器 DispatcherServlet 开始:

<!-- servlet请求分发器 -->
<servlet>
  <servlet-name>springMvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:servlet-context.xml</param-value>
  </init-param>
  <!-- 表示启动容器时初始化该Servlet -->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springMvc</servlet-name>
  <!-- 这是拦截请求, /代表拦截所有请求,拦截所有.do请求 -->
  <url-pattern>/</url-pattern>
</servlet-mapping>

DispatcherServlet UML继承关系图如下:

这里关注蓝线部分继承结构:DispatcherServlet–>FrameworkServlet–>HttpServletBean–>HttpServlet–>GenericServlet–>Servlet,对于请求核心时序图如下:

对于 web 请求的处理,大家都知道是通过继承 HttpServlet 重写其 service 方法,这里打开 DispatcherServlet 源码发现这里并没有看到我们要找的 service 方法,此时到父类 FrameworkServlet 查找如下:可以看到父类重写 HttpServlet service 方法。

FrameworkServlet#service

/**
 * Override the parent class implementation in order to intercept PATCH requests.
 */
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   
   HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
   if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
   
      processRequest(request, response);
   }
   else {
   
      super.service(request, response);
   }
}

从源码分析来看当请求方法为 patch 请求或者为 null 时执行 processRequest0 方法,其他情况则调用父类 service 方法,大家都知道 SpringMVC 请求大多请求是 get|post 请求为主,此时继续向上查看 FrameworkServlet 父类 HttpServletBean(抽象类继承 HttpServlet 并未重写 service 方法,所以向上继续寻找)–> HttpServlet service 方法:

HttpServlet#service

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
   
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
   
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
}


protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
   
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
   
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
   
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
   
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
   
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
   
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值