请求处理流程 (Processing)
DispatcherServlet 处理请求的具体流程如下:
- 绑定上下文:在请求中搜索并绑定
WebApplicationContext作为属性,以便控制器和流程中的其他元素可以使用它。默认情况下,它绑定在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE键下。 - 绑定区域解析器:将区域解析器(Locale Resolver)绑定到请求,以允许流程中的元素解析在处理请求(渲染视图、准备数据等)时要使用的区域。如果你不需要区域解析,则不需要配置它。
- 多部分请求处理:如果指定了多部分文件解析器(Multipart Resolver),则会检查请求是否包含多部分内容。如果发现多部分内容,请求将被包装在
MultipartHttpServletRequest中,以便流程中的其他元素进一步处理。 - 查找处理器:搜索合适的处理器(Handler)。如果找到了处理器,则执行与该处理器关联的执行链(包括预处理器、后处理器和控制器),以准备用于渲染的模型(Model)。对于注解控制器,响应也可以直接在
HandlerAdapter内部渲染,而不是返回视图。 - 视图渲染:如果返回了模型,则进行视图渲染。如果没有返回模型(可能是由于预处理器或后处理器出于安全等原因拦截了请求),则不会渲染视图,因为请求可能已经完成。
异常处理与缓存
- 异常处理:在
WebApplicationContext中声明的HandlerExceptionResolverBean 用于解析请求处理过程中抛出的异常。这些异常解析器允许自定义处理异常的逻辑。 - HTTP 缓存支持:处理器可以使用
WebRequest的checkNotModified方法,以及针对注解控制器的其他选项(如 ETag 和 Last-Modified 支持)。
初始化参数
你可以通过在 web.xml 文件中的 Servlet 声明中添加 Servlet 初始化参数(init-param 元素)来定制单个 DispatcherServlet 实例。支持的参数如下:
| 参数 | 说明 |
|---|---|
contextClass | 实现 ConfigurableWebApplicationContext 的类,由该 Servlet 实例化并进行本地配置。默认使用 XmlWebApplicationContext。 |
contextConfigLocation | 传递给上下文实例(由 contextClass 指定)的字符串,指示可以找到上下文的位置。该字符串可以包含多个路径(使用逗号作为分隔符)。如果多个路径中有重复定义的 Bean,则以后者为准。 |
namespace | WebApplicationContext 的命名空间。默认为 [servlet-name]-servlet。 |
throwExceptionIfNoHandlerFound | 当没找到处理请求的处理器时,是否抛出 NoHandlerFoundException。该异常可以被 HandlerExceptionResolver 捕获并处理。从 6.1 版本起,该属性默认为 true 且已弃用。 |
补充教学
1. 为什么“执行链 (Execution Chain)”如此关键?
很多人认为 DispatcherServlet 只是调用 Controller。实际上,它返回的是一个 HandlerExecutionChain 对象。
- 组成部分:它包含一个真正的 处理器 (Handler) 和一组 拦截器 (Interceptors)。
- 运行逻辑:
- 执行所有拦截器的
preHandle方法。 - 执行 Controller 方法。
- 执行所有拦截器的
postHandle方法。 - 视图渲染。
- 执行所有拦截器的
afterCompletion方法。 这种流水线设计让 Spring MVC 能够非侵入式地处理权限、日志、事务等横切关注点。
- 执行所有拦截器的
2. 多部分请求的“二次包装”
当你的应用需要处理文件上传时,MultipartResolver 会检查 HTTP 请求头中的 Content-Type 是否为 multipart/form-data。
- 如果是:它会将原始的
HttpServletRequest包装成MultipartHttpServletRequest。 - 好处:这个新对象提供了
getFile(String name)这样方便的 API。在你的 Controller 中,你可以直接申明MultipartFile类型的参数,Spring 会自动从这个包装类中提取文件数据。
3. 理解 checkNotModified 的性能优化
这是一个经常被忽略的性能杀手锏:
- 工作机制:当浏览器带上
If-Modified-Since或If-None-Match头时,你的 Controller 可以先查一下数据库数据的最后修改时间或指纹(ETag)。 - 效果:如果数据没变,调用
webRequest.checkNotModified(timestamp)会立即返回true。此时 Spring 会直接返回 304 Not Modified 响应,正文为空,完全跳过繁重的视图渲染和数据传输过程。这能极大节省带宽。