一、 请求处理
Spring MVC 是 Spring 框架的一部分,用于构建 Web 应用程序。它遵循 MVC(Model-View-Controller)设计模式,将应用程序分为模型(Model)、**视图(View)和控制器(Controller)**三个部分。
DispatcherServlet 处理流程:
- 当用户发起请求时,会将请求直接传入前端控制器
DispatcherServlet
,它是 Spring MVC 核心组件,负责接收请求并分发到各自的处理器中 - DispatcherServlet 接收到请求后,会调用
HandlerMapping
组件。HandlerMapping 根据请求的 URL 查找合适的处理器(Handler)。这里的Handler
通常是指Controller
中的方法,这些方法通常带有 @RequestMapping 注解(或其变体如 @GetMapping , @PostMapping 等)。 - DispatcherServlet 获取到合适的
Handler
后,会调用HandlerAdapter
组件。HandlerAdapter 负责执行处理器方法,并处理方法的返回值。HandlerAdapter
同样会去 Handler (Controller)中找那些带有 @RequestMapping 注解的方法 HandlerAdapter
调用 Controller 中的方法,执行业务逻辑。处理完业务后,会返回一个ModelAndView
对象给 HandlerAdapter。ModelAndView 对象中包含模型数据(Model
)和视图名称(View
)。- DispatcherServlet 从 HandlerAdapter 获取到
ModelAndView
对象后,会调用ViewResolver
组件。ViewResolver 负责将视图名称解析为实际的视图对象。 DispatcherServlet
获取到具体的视图对象后,将模型数据传递给视图对象View
进行渲染。- 渲染完成后,
DispatcherServlet
将生成的响应内容返回给用户,浏览器显示最终的页面。
- 客户端发出请求,会先经过 filter 过滤,通过的请求才能到达 DispatcherServlet。
- DispatcherServlet 通过 handlerMapping 找到请求对应的 handler,返回一个 HandlerExecutionChain 里面包含 interceptors 和 handler
- DispatcherServlet 通过 handlerAdapter 调用实际的 handler 处理业务逻辑, 返回 ModelAndView。里面会包含逻辑视图名和 model 数据。注意,在此之前和之后,会分别调用 interceptors 拦截处理
- 调用 viewResolver 将逻辑视图名解析成 view 返回
- 调用 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 生命周期
- Servlet 实例化后,Servlet 容器会调用
init
方法初始化。init 只会被调用一次,且必须成功执行才能提供服务。 - 客户端每次发出请求,Servlet 容器调用
service
方法处理请求。 - Servlet 被销毁前,Servlet 容器调用
destroy
方法。通常用来清理资源,比如内存,文件,线程等。
2. Filter
Filter 是过滤器,用于在客户端的请求访问后端资源之前,拦截这些请求;或者在服务器的响应发送回客户端之前,处理这些响应。只有通过 Filter 的请求才能被 Servlet 处理。
Filter 可以通过配置(xml 或 java-based)拦截特定的请求,在 Servlet 执行前后(由 chain.doFilter 划分)处理特定的逻辑,如权限过滤,字符编码,日志打印,Session 处理,图片转换等
Filter 生命周期
-
Filter 实例化后,Servlet 容器会调用
init
方法初始化。init 方法只会被调用一次,且成功执行(不抛出错误且没有超时)才能提供过滤功能 -
客户端每次发出请求,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 后执行"); }
-
Filter 生命周期结束时调用
destroy
方法,通常用来清理它所持有的资源,比如内存,文件,线程等。
3. Listener
Listener 是监听某个对象的状态变化的组件,是一种观察者模式。
被监听的对象可以是域对象 ServletContext, Session, Request
监听的内容可以是域对象的创建与销毁,域对象属性的变化
ServletContext | HttpSession | ServletRequest | |
---|---|---|---|
对象的创建与销毁 | ServletContextListener | HttpSessionListener | ServletRequestListener |
对象的属性的变化 | ServletContextAttributeListener | HttpSessionAttributeListener | ServletRequestAttributeListener |
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 类图
ApplicationContext 有一个抽象实现类 AbstractApplicationContext, 模板方法的设计模式。它有一个 refresh 方法,它定义了加载或初始化 bean 配置的基本流程。后面的实现类提供了不同的读取配置的方式,可以是 xml, annotation, web 等,并且可以通过模板方法定制自己的需求。
AbstractApplicationContext 有两个实现体系, 他们的区别是每次 refresh 时是否会创建一个新的 DefaultListableBeanFactory。DefaultListableBeanFactory 是实际存放 bean 的容器, 提供 bean 注册功能。
- AbstractRefreshableApplicationContext 这个 refreshable 并不是指 refresh 这个方法,而是指 refreshBeanFactory 这个方法。他会在每次 refresh 时创建一个新的 BeanFactory(DefaultListableBeanFactory)用于存放 bean,然后调用 loadBeanDefinitions 将 bean 加载到新创建的 BeanFactory。
- GenericApplicationContext 内部持有一个 DefaultListableBeanFactory, 所以可以提前将 Bean 加载到 DefaultListableBeanFactory, 它也有 refreshBeanFactory 方法,但是这个方法啥也不做。
根据读取配置的方式,可以分成 3 类,基于 xml 的配置, 基于 annotation 的配置和基于 java-based 的配置
- 基于 xml 的配置使用 xml 作为配置方式, 此类的名字都含有 Xml, 比如从文件系统路径读取配置的 FilePathXmlApplicationContext, 从 ClassPath 读取配置的 ClassPathXmlApplicationContext, 基于 Web 的 XmlWebApplicationContext 等
- 基于注解的配置通过扫描指定包下面具有某个注解的类,将其注册到 bean 容器,相关注解有 @Component, @Service, @Controller, @Repository,@Named 等
- java-based 的配置方式目前是大势所趋,结合注解的方式使用简单方便易懂,主要是 @Configuration 和 @Bean
上面几个类是基础类,下面是 SpringMVC 相关的 WebApplicationContext
-
XmlWebApplicationContext 和 AnnotationConfigWebApplicationContext 继承自 AbstractRefreshableApplicationContext,表示它们会在 refresh 时新创建一个 DefaultListableBeanFactory, 然后 loadBeanDefinitions。 它们分别从 xml 和 注解类(通常是 @Configuration 注解的配置类)中读取配置信息。
-
XmlEmbeddedWebApplicationContext 和 AnnotationConfigEmbeddedWebApplicationContext 继承自 GenericApplicationContext,表示他们内部持有一个 DefaultListableBeanFactory, 从名字可以看出它们是用于 “Embedded” 方面的,即 SpringBoot 嵌入容器所使用的 WebApplicationContext
-
SpringMVC 应用中几乎所有的类都交由 WebApplicationContext 管理,包括业务方面的 @Controller, @Service, @Repository 注解的类, ServletContext, 文件处理 multipartResolver, 视图解析器 ViewResolver, 处理器映射器 HandleMapping 等。