当前位置: 首页 > web >正文

深入理解 Spring MVC:DispatcherServlet 与视图解析机制​

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;// 继承自 FrameworkServlet 的 DispatcherServlet 类,作为前端控制器
public class DispatcherServlet extends FrameworkServlet {// 前端控制器的核心方法,处理请求,返回视图,渲染视图,都是在这个方法中完成的。protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 假设这里有对请求进行预处理HttpServletRequest processedRequest = request;// 模拟处理器适配器和映射处理器HandlerAdapter ha = new SimpleHandlerAdapter();HandlerExecutionChain mappedHandler = new HandlerExecutionChain(new SimpleController());// 根据请求路径调用映射的处理器方法,处理器方法执行结束之后,返回逻辑视图名称// 返回逻辑视图名称之后,DispatcherServlet会将 逻辑视图名称ViewName + Model,将其封装为ModelAndView对象。ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 这行代码的作用是处理视图processDispatchResult(processedRequest, response, mappedHandler, mv, null);}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv,Exception dispatchException) throws Exception {// 渲染页面(将模板字符串转换成html代码响应到浏览器)render(mv, request, response);}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {String viewName = mv.getViewName();// 这个方法的作用是将 逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象ViewView view = resolveViewName(viewName, mv.getModelInternal(), Locale.getDefault(), request);// 真正的将模板字符串转换成HTML代码,并且将HTML代码响应给浏览器。(真正的渲染。)view.render(mv.getModelInternal(), request, response);}protected View resolveViewName(String viewName, Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {// 其实这一行代码才是真正起作用的:将 逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象ViewViewResolver viewResolver = new SimpleViewResolver();// 如果使用的是Thymeleaf,那么返回的视图对象:ThymeleafView对象。View view = viewResolver.resolveViewName(viewName, locale);return view;}
}// 处理器适配器接口
interface HandlerAdapter {ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}// 简单处理器适配器实现
class SimpleHandlerAdapter implements HandlerAdapter {@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof SimpleController) {SimpleController controller = (SimpleController) handler;return controller.handleRequest(request, response);}return null;}
}// 处理器执行链类
class HandlerExecutionChain {private final Object handler;public HandlerExecutionChain(Object handler) {this.handler = handler;}public Object getHandler() {return handler;}
}// 简单控制器类
class SimpleController {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {// 模拟返回逻辑视图名称和模型return new ModelAndView("testView", null);}
}// 模型和视图类
class ModelAndView {private final String viewName;private final Map<String, Object> model;public ModelAndView(String viewName, Map<String, Object> model) {this.viewName = viewName;this.model = model;}public String getViewName() {return viewName;}public Map<String, Object> getModelInternal() {return model;}
}// 这是一个接口(负责视图解析的)
interface ViewResolver {// 如果使用Thymeleaf,那么该接口的实现类就是:ThymeleafViewResolver// 这个方法就是将:逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象ViewView resolveViewName(String viewName, Locale locale) throws Exception;
}// 简单视图解析器实现
class SimpleViewResolver implements ViewResolver {@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return new SimpleView();}
}// 这是一个接口(负责将 模板字符串 转换成HTML代码,响应给浏览器)
interface View {void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;
}// 简单视图实现
class SimpleView implements View {@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {response.setContentType("text/html;charset=UTF-8");response.getWriter().write("<html><body><h1>Simple View Rendered</h1></body></html>");}
}// 模拟的 FrameworkServlet 类
abstract class FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 空实现,由子类实现具体逻辑}
}

代码功能概述

这段 Java 代码实现了一个简单的前端控制器 DispatcherServlet,它是整个 Web 请求处理流程的核心。其主要功能是接收客户端请求,根据请求路径调用对应的处理器方法,获取逻辑视图名称,将逻辑视图名称转换为物理视图名称,最终将模板字符串渲染成 HTML 代码并响应给客户端。同时,代码中定义了两个核心接口 ViewResolver 和 View,分别负责视图解析和视图渲染。

代码解释

  1. DispatcherServlet 类:作为前端控制器,继承自 FrameworkServletdoDispatch 方法是核心方法,负责接收请求、调用处理器方法、获取逻辑视图名称并封装为 ModelAndView 对象,最后调用 processDispatchResult 方法处理视图。processDispatchResult 方法调用 render 方法进行视图渲染。render 方法通过 resolveViewName 方法将逻辑视图名称转换为物理视图名称并获取视图对象,最后调用视图对象的 render 方法进行实际渲染。
  2. ViewResolver 接口:定义了 resolveViewName 方法,用于将逻辑视图名称转换为物理视图名称并返回视图对象。
  3. View 接口:定义了 render 方法,用于将模板字符串转换为 HTML 代码并响应给浏览器。
  4. 其他辅助类:为了使代码能够完整运行,添加了 HandlerAdapterHandlerExecutionChainSimpleControllerModelAndViewSimpleViewResolver 和 SimpleView 等类,分别模拟处理器适配器、处理器执行链、控制器、模型和视图、视图解析器和视图的实现。

结论

如果你想实现自己的视图,至少需要编写两个类:一个类实现 ViewResolver 接口,实现其中的 resolveViewName 方法;另一个类实现 View 接口,实现其中的 render 方法。这样就可以自定义视图的解析和渲染逻辑。



深入理解 Spring MVC:DispatcherServlet 与视图解析机制​



在 Spring MVC 框架中,DispatcherServlet作为前端控制器,是整个请求处理流程的核心枢纽。它负责协调各个组件完成请求接收、处理器调用、视图解析和页面渲染等关键步骤。本文将通过源码级解析,带你理解 DispatcherServlet 的核心逻辑,并揭秘视图解析的两大关键接口ViewResolver与View的工作原理。​

一、DispatcherServlet 核心流程:请求处理的 "总指挥"​

DispatcherServlet继承自FrameworkServlet,其核心逻辑集中在doDispatch方法中,该方法串联了从请求接收到视图渲染的完整流程:​

1. 处理器调用与 ModelAndView 生成

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 1. 预处理请求(示例代码简化处理)HttpServletRequest processedRequest = request;// 2. 模拟处理器适配与映射(实际由Spring容器管理)HandlerAdapter ha = new SimpleHandlerAdapter();HandlerExecutionChain mappedHandler = new HandlerExecutionChain(new SimpleController());// 3. 调用处理器方法,获取逻辑视图名+模型数据ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 4. 处理视图渲染逻辑processDispatchResult(processedRequest, response, mappedHandler, mv, null);
}
  • 处理器适配:通过HandlerAdapter适配不同类型的处理器(如 @Controller 标注的控制器)​
  • 关键产出:ModelAndView对象封装了逻辑视图名(如 "user/list")和模型数据(如 Map 类型的参数)​

2. 视图处理的关键步骤

private void processDispatchResult(...) {// 核心:调用render方法进行视图渲染render(mv, request, response); 
}protected void render(ModelAndView mv, ...) throws Exception {// 1. 解析逻辑视图名 -> 物理视图对象View view = resolveViewName(mv.getViewName(), ...); // 2. 执行实际的视图渲染view.render(mv.getModelInternal(), request, response); 
}

这里体现了 "两步走" 的视图处理逻辑:先解析视图类型,再执行渲染。接下来我们深入这两个核心环节。​

二、两大核心接口:视图解析的 "左右护法"​

1. ViewResolver:逻辑视图到物理视图的桥梁

public interface ViewResolver {// 将逻辑视图名(如"home")转换为具体的View实现类View resolveViewName(String viewName, Locale locale) throws Exception;
}
  • 典型实现:Thymeleaf 对应ThymeleafViewResolver,FreeMarker 对应FreeMarkerViewResolver​
  • 核心逻辑:根据视图名拼接物理路径(如添加前缀 "/WEB-INF/views/" 和后缀 ".html"),并创建对应的 View 实例​

2. View:模板渲染的实际执行者

public interface View {// 将模型数据渲染为HTML并响应给浏览器void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
  • 核心职责:​
  1. 加载模板文件(如 Thymeleaf 模板)​
  1. 填充模型数据到模板​
  1. 生成最终的 HTML 内容并写入响应流​

三、完整代码示例:模拟 Spring MVC 核心组件​

为了完整呈现流程,我们补充必要的辅助类(实际 Spring 中由框架提供):​

1. 处理器相关组件

// 处理器适配器(模拟Spring的HandlerAdapter)
interface HandlerAdapter {ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}// 简单控制器(返回逻辑视图名"testView")
class SimpleController {public ModelAndView handleRequest(...) {return new ModelAndView("testView", null); // 无模型数据}
}// 模型与视图封装类
class ModelAndView {private final String viewName;private final Map<String, Object> model;// 构造器、getter方法省略
}

2. 自定义视图解析与渲染(简化实现)

// 简单视图解析器(返回自定义View实例)
class SimpleViewResolver implements ViewResolver {@Overridepublic View resolveViewName(String viewName, Locale locale) {return new SimpleView(); // 实际应根据视图名创建不同View}
}// 简单视图实现(直接返回固定HTML)
class SimpleView implements View {@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html;charset=UTF-8");response.getWriter().write("<h1>Custom View Rendered!</h1>");}
}

四、如何实现自定义视图?两步走策略​

如果你需要集成新的视图技术(如自定义模板引擎),只需完成以下两步:​

1. 实现 ViewResolver 接口

public class MyViewResolver implements ViewResolver {private String prefix = "/views/";private String suffix = ".mytpl";@Overridepublic View resolveViewName(String viewName, Locale locale) {return new MyView(prefix + viewName + suffix); // 拼接物理路径}
}

2. 实现 View 接口

public class MyView implements View {private String templatePath;public MyView(String templatePath) {this.templatePath = templatePath;}@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 1. 读取模板文件String template = Files.readString(Paths.get(templatePath));// 2. 数据填充(假设模板语法为{{key}})String html = template.replaceAll("\\{\\{([^\\}]+)\\}\\}", model.get("$1").toString());// 3. 输出响应response.getWriter().write(html);}
}

五、总结:理解框架设计的核心思想​

通过剖析 DispatcherServlet 的源码逻辑,我们可以提炼出 Spring MVC 的两大设计精髓:​

  1. 责任链模式:DispatcherServlet 作为核心协调者,将请求处理分解为处理器调用、视图解析、视图渲染等独立步骤,各组件通过接口解耦​
  1. 策略模式:通过 ViewResolver 和 View 接口的不同实现,支持灵活切换视图技术(Thymeleaf/FreeMarker/ 自定义)​

当我们需要扩展框架功能时(如支持新视图),只需遵循既定接口规范,即可无缝集成到现有流程中。这种 "约定大于配置" 的设计思想,正是 Spring 框架保持强大扩展性的关键所在。​

理解这些核心机制后,我们不仅能更熟练地使用 Spring MVC 的现有功能,还能在遇到复杂需求时,基于框架扩展点实现优雅的解决方案。下次当你在配置文件中写下ThymeleafViewResolver时,应该能清晰联想到背后整个视图解析与渲染的流程了吧?

http://www.xdnf.cn/news/3864.html

相关文章:

  • 快速弄懂POM设计模式
  • 1991年-2023年 上市公司-重污染企业数据 -社科数据
  • GitHub 趋势日报 (2025年05月03日)
  • 多模态大语言模型arxiv论文略读(五十九)
  • STM32教程:ADC原理及程序(基于STM32F103C8T6最小系统板标准库开发)*详细教程*
  • 数电填空题整理(适用期末考试)
  • Linux网络编程:套接字
  • C++类_匿名类
  • 从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 2 |蜂窝 LBS on Tracker:从 AT 命令到定位结果
  • 今天python练习题
  • MYSQL-联合查询
  • 【前端】【总复习】HTML
  • 基于 ESP32 和 GC9D01 0.71寸TFT屏幕的逼真眼睛与写轮眼动态显示
  • Spring Boot Jpa封装快速构建Specification、OrderBy、Pageable的查询条件
  • 【Python】一直没搞懂生成器是什么。。
  • 【25软考网工】第五章(5)ICMP和ICMPv6、NDP、IP组播技术和MPLS
  • JavaScript基础-分支流程控制
  • strstr()和strpbrk()函数的区别
  • 学习黑客开源情报
  • Go语言接口实现面对对象的三大特征
  • 基于大模型的隐睾(睾丸可触及)预测及临床干预策略研究报告
  • spring中的@Profile注解详解
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】2.3 窗口函数与高级聚合(ROW_NUMBER()/RANK()/SUM() OVER())
  • 民法学学习笔记(个人向) Part.3
  • Python 库 petrel_client.client 浅入浅出
  • 【CISCO】什么是静态路由(Static Route)?ip route 192.0.1.0 255.255.255.0 200.0.0.1
  • 一周学会Pandas2 Python数据处理与分析-Pandas2复杂数据查询操作
  • 【前端】【面试】在 Nuxt.js SSR/SSG 应用开发的 SEO 优化方面,你采取了哪些具体措施来提高页面在搜索引擎中的排名?
  • NPP库中libnppist模块介绍
  • 利用flask设计接口