【Spring MVC】DispatcherServlet 请求处理流程

一、 请求处理

Spring MVC 是 Spring 框架的一部分,用于构建 Web 应用程序。它遵循 MVC(Model-View-Controller)设计模式,将应用程序分为模型(Model)、**视图(View)控制器(Controller)**三个部分。

DispatcherServlet 处理流程:

在这里插入图片描述

  1. 当用户发起请求时,会将请求直接传入前端控制器 DispatcherServlet ,它是 Spring MVC 核心组件,负责接收请求并分发到各自的处理器中
  2. DispatcherServlet 接收到请求后,会调用 HandlerMapping 组件。HandlerMapping 根据请求的 URL 查找合适的处理器(Handler)。这里的 Handler 通常是指 Controller 中的方法,这些方法通常带有 @RequestMapping 注解(或其变体如 @GetMapping , @PostMapping 等)。
  3. DispatcherServlet 获取到合适的 Handler 后,会调用 HandlerAdapter 组件。HandlerAdapter 负责执行处理器方法,并处理方法的返回值。HandlerAdapter同样会去 Handler (Controller)中找那些带有 @RequestMapping 注解的方法
  4. HandlerAdapter 调用 Controller 中的方法,执行业务逻辑。处理完业务后,会返回一个 ModelAndView 对象给 HandlerAdapter。ModelAndView 对象中包含模型数据(Model)和视图名称(View)。
  5. DispatcherServlet 从 HandlerAdapter 获取到 ModelAndView 对象后,会调用 ViewResolver 组件。ViewResolver 负责将视图名称解析为实际的视图对象。
  6. DispatcherServlet 获取到具体的视图对象后,将模型数据传递给视图对象View进行渲染。
  7. 渲染完成后,DispatcherServlet 将生成的响应内容返回给用户,浏览器显示最终的页面。

在这里插入图片描述

  1. 客户端发出请求,会先经过 filter 过滤,通过的请求才能到达 DispatcherServlet。
  2. DispatcherServlet 通过 handlerMapping 找到请求对应的 handler,返回一个 HandlerExecutionChain 里面包含 interceptors 和 handler
  3. DispatcherServlet 通过 handlerAdapter 调用实际的 handler 处理业务逻辑, 返回 ModelAndView。里面会包含逻辑视图名和 model 数据。注意,在此之前和之后,会分别调用 interceptors 拦截处理
  4. 调用 viewResolver 将逻辑视图名解析成 view 返回
  5. 调用 view.render 渲染视图,写进 response。然后 interceptors 和 filter 依次拦截处理,最后返回给客户端

HandlerInterceptor

拦截器。在 handler 执行前后及视图渲染后执行拦截,可以注册不同的 interceptor 定制工作流程

public interface HandlerInterceptor {

    // 在 handler 执行前拦截,返回 true 才能继续调用下一个 interceptor 或者 handler
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    // 在 handler 执行后,视图渲染前进行拦截处理
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }

    // 视图渲染后,请求完成后进行处理,可以用来清理资源
    // 除非 preHandle 放回 false,否则一定会执行,即使发生错误
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }

}

二、设计原理

Servlet 规范

SpringMVC 是基于 Servlet 的。

Servlet 是运行在 web 服务器上的程序,它接收并响应来自 web 客户端的请求(通常是 HTTP 请求)。

Servlet 规范有三个主要的技术点: Servlet, Filter, Listener

1. Servlet

Servlet 是实现 Servlet 接口的程序。对于 HTTP, 通常继承 javax.servlet.http.HttpServlet, 可以为不同的 URL 配置不同的 Servlet。Servlet 是"单例"的,所有请求共用一个 Servlet, 因此对于共享变量(比如实例变量),需要自己保证其线程安全性。DispatcherServlet 便是一个 Servlet。

Servlet 生命周期

  1. Servlet 实例化后,Servlet 容器会调用 init 方法初始化。init 只会被调用一次,且必须成功执行才能提供服务。
  2. 客户端每次发出请求,Servlet 容器调用 service 方法处理请求。
  3. Servlet 被销毁前,Servlet 容器调用 destroy 方法。通常用来清理资源,比如内存,文件,线程等。
2. Filter

Filter 是过滤器,用于在客户端的请求访问后端资源之前,拦截这些请求;或者在服务器的响应发送回客户端之前,处理这些响应。只有通过 Filter 的请求才能被 Servlet 处理。

Filter 可以通过配置(xml 或 java-based)拦截特定的请求,在 Servlet 执行前后(由 chain.doFilter 划分)处理特定的逻辑,如权限过滤,字符编码,日志打印,Session 处理,图片转换等

Filter 生命周期

  1. Filter 实例化后,Servlet 容器会调用 init 方法初始化。init 方法只会被调用一次,且成功执行(不抛出错误且没有超时)才能提供过滤功能

  2. 客户端每次发出请求,Servlet 容器调用 doFilter 方法拦截请求。

    public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
    
        // 客户端的请求访问后端资源之前的处理逻辑
        System.out.println("我在 Servlet 前执行");
    
        // 把请求传回过滤链,即传给下一个 Filter, 或者交给 Servlet 处理
        chain.doFilter(request,response);
    
        // 服务器的响应发送回客户端之前的处理逻辑
        System.out.println("我在 Servlet 后执行");
    }
    
  3. Filter 生命周期结束时调用 destroy 方法,通常用来清理它所持有的资源,比如内存,文件,线程等。

3. Listener

Listener 是监听某个对象的状态变化的组件,是一种观察者模式。

被监听的对象可以是域对象 ServletContext, Session, Request

监听的内容可以是域对象的创建与销毁,域对象属性的变化

ServletContextHttpSessionServletRequest
对象的创建与销毁ServletContextListenerHttpSessionListenerServletRequestListener
对象的属性的变化ServletContextAttributeListenerHttpSessionAttributeListenerServletRequestAttributeListener

WebApplicationContext

SpringMVC 用 Spring 化的方式来管理 web 请求中的各种对象。

什么是 Spring 化? IOC 和 AOP, 这不是本文的重点,具体自行查阅。

SpringMVC 会通过 WebApplicationContext 来管理服务器请求中涉及到的各种对象和他们之间的依赖关系。

WebApplicationContext 继承自 ApplicationContext, 它定义了一些新的作用域,提供了 getServletContext 接口。

public interface WebApplicationContext extends ApplicationContext {

    // 根容器名,作为 key 存储在 ServletContext 中; ServletContext 持有的 WebApplicationContext
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

    /**
     * 这三个是 WebApplicationContext 特有的作用域
     * 通过 WebApplicationContextUtils.registerWebApplicationScopes 注册相应的处理器
     */
    String SCOPE_REQUEST = "request";
    String SCOPE_SESSION = "session";
    String SCOPE_APPLICATION = "application";

    /**
     * ServletContext 在 WebApplicationContext 中的名字
     * 通过 WebApplicationContextUtils.registerEnvironmentBeans 注册到 WebApplicationContext 中
     */
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";

    /**
      * ServletContext 和 ServletConfig 配置参数在 WebApplicationContext 中的名字
      * ServletConfig 的参数具有更高的优先级,会覆盖掉 ServletContext 中的参数
      * 值为 Map<String, String> 结构
      */
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";

    /**
      * ServletContext 属性信息在 WebApplicationContext 中的名字
      * 值为 Map<String, String> 结构
      * 属性是用来描述 ServletContext 本身的一些信息的
      */
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";


    /**
     * 获取 ServletContext 接口
     */
    @Nullable
    ServletContext getServletContext();

}
WebApplicationContext 类图

img

ApplicationContext 有一个抽象实现类 AbstractApplicationContext, 模板方法的设计模式。它有一个 refresh 方法,它定义了加载或初始化 bean 配置的基本流程。后面的实现类提供了不同的读取配置的方式,可以是 xml, annotation, web 等,并且可以通过模板方法定制自己的需求。

AbstractApplicationContext 有两个实现体系, 他们的区别是每次 refresh 时是否会创建一个新的 DefaultListableBeanFactory。DefaultListableBeanFactory 是实际存放 bean 的容器, 提供 bean 注册功能。

  1. AbstractRefreshableApplicationContext 这个 refreshable 并不是指 refresh 这个方法,而是指 refreshBeanFactory 这个方法。他会在每次 refresh 时创建一个新的 BeanFactory(DefaultListableBeanFactory)用于存放 bean,然后调用 loadBeanDefinitions 将 bean 加载到新创建的 BeanFactory。
  2. GenericApplicationContext 内部持有一个 DefaultListableBeanFactory, 所以可以提前将 Bean 加载到 DefaultListableBeanFactory, 它也有 refreshBeanFactory 方法,但是这个方法啥也不做。

根据读取配置的方式,可以分成 3 类,基于 xml 的配置, 基于 annotation 的配置基于 java-based 的配置

  1. 基于 xml 的配置使用 xml 作为配置方式, 此类的名字都含有 Xml, 比如从文件系统路径读取配置的 FilePathXmlApplicationContext, 从 ClassPath 读取配置的 ClassPathXmlApplicationContext, 基于 Web 的 XmlWebApplicationContext 等
  2. 基于注解的配置通过扫描指定包下面具有某个注解的类,将其注册到 bean 容器,相关注解有 @Component, @Service, @Controller, @Repository,@Named 等
  3. java-based 的配置方式目前是大势所趋,结合注解的方式使用简单方便易懂,主要是 @Configuration 和 @Bean

上面几个类是基础类,下面是 SpringMVC 相关的 WebApplicationContext

  1. XmlWebApplicationContext 和 AnnotationConfigWebApplicationContext 继承自 AbstractRefreshableApplicationContext,表示它们会在 refresh 时新创建一个 DefaultListableBeanFactory, 然后 loadBeanDefinitions。 它们分别从 xml 和 注解类(通常是 @Configuration 注解的配置类)中读取配置信息。

  2. XmlEmbeddedWebApplicationContext 和 AnnotationConfigEmbeddedWebApplicationContext 继承自 GenericApplicationContext,表示他们内部持有一个 DefaultListableBeanFactory, 从名字可以看出它们是用于 “Embedded” 方面的,即 SpringBoot 嵌入容器所使用的 WebApplicationContext

  3. SpringMVC 应用中几乎所有的类都交由 WebApplicationContext 管理,包括业务方面的 @Controller, @Service, @Repository 注解的类, ServletContext, 文件处理 multipartResolver, 视图解析器 ViewResolver, 处理器映射器 HandleMapping 等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值