二十八、面向对象底层逻辑-SpringMVC九大组件之ViewResolver接口设计

在 Spring MVC 框架中,视图解析器(ViewResolver)是连接控制器逻辑与具体视图技术的核心纽带。它通过抽象化的接口设计,将视图的渲染逻辑与业务逻辑解耦,使开发者能够灵活支持 JSP、Thymeleaf、FreeMarker 等多种视图技术,同时保持框架的高度扩展性。本文将从接口设计的角度,深入探讨 ViewResolver 的设计哲学、实现机制及其在 Spring MVC 架构中的核心价值。


一、设计背景与核心问题

在 Web 应用开发中,控制器的职责是处理请求并返回逻辑视图名称(如 "home" 或 "user/profile"),而如何将逻辑视图名称转换为具体的视图对象(如 HTML 页面、JSON 数据),则属于视图层的职责。早期的 MVC 框架往往将视图解析逻辑硬编码在控制器或前端控制器中,导致以下问题:

  1. 技术耦合:更换视图技术(如从 JSP 切换到 Thymeleaf)需要修改大量代码。

  2. 扩展困难:新增自定义视图类型(如 PDF 导出)需侵入框架核心逻辑。

  3. 配置冗余:不同视图技术的配置分散,难以统一管理。

ViewResolver 的设计目标,是通过策略模式(Strategy Pattern)抽象视图解析过程,实现以下目标:

  • 解耦视图技术与业务逻辑

  • 支持多视图技术共存

  • 提供统一的扩展接口


二、接口设计的核心思想
1. 单一职责原则(SRP)

ViewResolver 接口仅定义一个核心方法:

public interface ViewResolver {View resolveViewName(String viewName, Locale locale) throws Exception;
}

其唯一职责是将逻辑视图名称(viewName)和区域(Locale)解析为具体的 View 对象。这种设计使得每个 ViewResolver 实现类只需关注特定类型的视图解析逻辑,例如:

  • InternalResourceViewResolver 解析 JSP 页面。

  • ThymeleafViewResolver 解析 Thymeleaf 模板。

2. 开闭原则(OCP)

通过接口抽象,ViewResolver 允许开发者在不修改现有代码的前提下,扩展新的视图解析方式。例如,集成 FreeMarker 只需实现 ViewResolver 并配置对应的 FreeMarkerViewResolver,无需调整控制器或 DispatcherServlet 的逻辑。

3. 模块化与组合性

Spring MVC 支持同时注册多个 ViewResolver,并通过 Ordered 接口定义解析器的优先级。这种设计使得应用可以灵活组合多种视图技术,例如:

  • 优先使用 Thymeleaf 解析 HTML 视图。

  • 若解析失败,则回退到 JSP 视图。

public class ThymeleafViewResolver implements ViewResolver, Ordered {private int order = 1;  // 优先级高于默认的 InternalResourceViewResolver(order=Integer.MAX_VALUE)// 实现 resolveViewName 方法...
}

 3. 接口设计哲学

  • 服务域对象:ViewResolver为服务域对象,以单例模式加载并缓存,单实例服务于所有调用,通过多态将View的包装过程暴露给扩展者。

  • 实体域对象:resolve方法输出的View属于实体域,View的实现需要线程安全,缓存复用实例。

  • 元数据对象:resolve方法的入参是视图名,属于View的元数据。


三、核心实现类的设计分析
1. InternalResourceViewResolver:JSP 的经典支持

这是最常用的视图解析器,用于解析 JSP 页面。其设计特点包括:

  • 前缀与后缀配置:通过 setPrefix("/WEB-INF/views/") 和 setSuffix(".jsp") 定义视图路径规则。

  • 内部转发机制:使用 RequestDispatcher 将请求转发到 JSP 页面,而非直接渲染,从而支持 JSP 与 Servlet 容器的协作。

public class InternalResourceViewResolver extends UrlBasedViewResolver {protected AbstractUrlBasedView buildView(String viewName) {return new InternalResourceView();  // 实际生成 JSP 视图对象}
}
2. ContentNegotiatingViewResolver:内容协商的智能化

此解析器根据请求的媒体类型(如 Accept 头)自动选择最佳视图,支持 RESTful 接口的多格式响应(如 JSON、XML)。其核心设计包括:

  • 视图解析器委托链:将实际解析工作委托给其他 ViewResolver

  • 媒体类型匹配:通过 ContentNegotiationManager 确定客户端支持的视图类型。

public class ContentNegotiatingViewResolver implements ViewResolver, Ordered {private List<ViewResolver> viewResolvers;public View resolveViewName(String viewName, Locale locale) {List<View> candidateViews = new ArrayList<>();for (ViewResolver resolver : viewResolvers) {View view = resolver.resolveViewName(viewName, locale);if (view != null) candidateViews.add(view);}// 根据媒体类型选择最佳视图return selectBestView(request, candidateViews);}
}
3. AbstractTemplateViewResolver:模板引擎的统一抽象

针对 Thymeleaf、FreeMarker 等模板引擎,Spring MVC 提供了 AbstractTemplateViewResolver 作为基类,其设计亮点包括:

  • 模板文件定位:统一处理模板路径、编码和缓存配置。

  • 模板处理器注入:与具体模板引擎(如 TemplateEngine)解耦,通过子类实现细节。

public abstract class AbstractTemplateViewResolver extends UrlBasedViewResolver {protected abstract AbstractTemplateView buildView(String viewName);
}

四、协作流程与架构整合
1. 视图解析的完整流程
  1. 控制器返回逻辑视图名

    @GetMapping("/user")
    public String userProfile(Model model) {model.addAttribute("user", getUser());return "user/profile";  // 逻辑视图名
    }
  2. DispatcherServlet 调用 ViewResolver:遍历所有注册的 ViewResolver,按优先级调用 resolveViewName()

  3. 生成 View 对象:首个成功解析的 ViewResolver 返回 View 实例(如 ThymeleafView)。

  4. 视图渲染:调用 View.render() 方法,将模型数据写入响应(如生成 HTML)。

2. 与 View 接口的协作

View 接口定义了渲染行为的抽象:

public interface View {String getContentType();void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
  • 模板引擎视图:如 ThymeleafView 会调用模板引擎的 process() 方法。

  • 静态资源视图:如 ResourceBundleViewResolver 可直接返回文件内容。


五、自定义 ViewResolver 的设计实践
1. 场景:支持 Markdown 渲染为 HTML

假设需要将控制器返回的 Markdown 文件动态渲染为 HTML 页面,可通过以下步骤实现:

步骤 1:定义 MarkdownView
public class MarkdownView extends AbstractUrlBasedView {@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {String markdownContent = loadMarkdownFile(getUrl());  // 加载 .md 文件String html = convertMarkdownToHtml(markdownContent); // 转换为 HTMLresponse.getWriter().write(html);}
}
步骤 2:实现 MarkdownViewResolver
public class MarkdownViewResolver extends UrlBasedViewResolver {public MarkdownViewResolver() {setViewClass(MarkdownView.class);  // 指定视图类型setPrefix("/WEB-INF/markdown/");   // Markdown 文件路径前缀setSuffix(".md");                  // 文件后缀}
}
步骤 3:注册并配置
@Configuration
public class WebConfig implements WebMvcConfigurer {@Beanpublic MarkdownViewResolver markdownViewResolver() {MarkdownViewResolver resolver = new MarkdownViewResolver();resolver.setOrder(0);  // 优先级高于其他解析器return resolver;}
}
2. 效果验证

控制器方法返回 "user-guide" 时,MarkdownViewResolver 将解析 /WEB-INF/markdown/user-guide.md 文件并渲染为 HTML。


六、设计启示与最佳实践
  1. 接口抽象的价值ViewResolver 和 View 的分离,体现了“抽象接口定义契约,具体实现处理细节”的设计原则。

  2. 组合优于继承:通过组合多个 ViewResolver 实现多视图支持,而非通过复杂的继承体系。

  3. 性能优化:部分 ViewResolver(如 UrlBasedViewResolver)会缓存已解析的 View 对象,避免重复解析开销。


七、总结

ViewResolver 的设计是 Spring MVC 框架中“面向接口编程”思想的典范。它通过统一的抽象层,将视图技术的多样性与业务逻辑解耦,使开发者能够自由切换、扩展视图实现,同时保持框架核心的简洁性。无论是支持主流的模板引擎,还是集成自定义的渲染逻辑,ViewResolver 都展现了一种高度灵活、可扩展的设计模式。理解其设计哲学,不仅有助于更高效地使用 Spring MVC,也为构建可维护、可扩展的系统架构提供了重要参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LiveWallpaperMacOS:让你的 Mac 桌面动起来

随着桌面美化需求的不断提升,用户对于桌面壁纸的要求已经不再局限于静态图片。越来越多的 Mac 用户希望桌面能像 Windows 一样,拥有动态壁纸,展现个性、提升体验。LiveWallpaperMacOS 正是这样一款让你的 Mac 桌面焕发活力的开源项目。 本文将详细介绍 LiveWallpaperMacOS …

豆瓣电视剧数据工程实践:从爬虫到智能存储的技术演进(含完整代码)

通过网盘分享的文件&#xff1a;资料 链接: https://pan.baidu.com/s/1siOrGmM4n-m3jv95OCea9g?pwd4jir 提取码: 4jir 1. 引言 1.1 选题背景 在影视内容消费升级背景下&#xff0c;豆瓣电视剧榜单作为国内最具影响力的影视评价体系&#xff0c;其数据价值体现在&#xff1a…

集成均衡功能电池保护芯片在大功率移动电源的应用,创芯微CM1341-DAT、杰华特JW3312、赛微微电CW1244、中颖SH366006

一文了解集成均衡功能电池保护IC在大功率移动电源的应用 创芯微CM1341-DAT 创芯微CM1341-DAT是一款专用于4串锂离子/磷酸铁锂电池的保护芯片&#xff0c;内置有高精度电压检测电路和电流检测电路。通过检测各节电池的电压、充放电电流及温度等信息&#xff0c;实现电池过充电…

PHP生成pdf方法

1&#xff1a;第一种方法&#xff1a; 主要使用PHP的扩展 【 “spatie/browsershot”: “3.57”】 使用这个扩展生成PDF需要环境安装以下依赖 1.1&#xff1a;NPM【版本&#xff1a;9.2.0】 1.2&#xff1a;NODE【版本&#xff1a;v18.19.1】 1.3&#xff1a;puppeteer【npm in…

联通专线加持!亿林网络 24 核 32G 裸金属服务器,千兆共享带宽适配中小型企业 IT 架构

在当今数字化时代&#xff0c;企业的业务运营越来越依赖高效、稳定的 IT 架构。对于中小型企业而言&#xff0c;如何在有限的预算内构建强大且可靠的 IT 基础设施&#xff0c;是一项关键挑战。亿林网络推出的 24 核 32G 裸金属服务器&#xff0c;搭配联通专线和千兆共享带宽&am…

SQL计算列

SqlServer: ALTER TABLE KC_BILLHEAD ADD bill_no AS coalesce(billno , ) PERSISTED; 这是一个SQL语句&#xff0c;用于向表KC_BILLHEAD添加一个计算列bill_no。让我解释一下这个语句的各个部分&#xff1a; ALTER TABLE KC_BILLHEAD - 修改表KC_BILLHEAD的结构 ADD bill_n…

利用海外代理IP,做Twitter2026年全球趋势数据分析

近年来&#xff0c;社交媒体趋势分析逐渐成为品牌监控、市场洞察和消费者研究的必备工具。而当谈到全球趋势数据分析&#xff0c;很多人都会立即想到 Twitter趋势&#xff08;逼近连美丽国的总统都喜欢在上面发表自己的看法- -!!!&#xff09;。Twitter趋势&#xff0c;即Twitt…

【Vue3】Vue3 + TypeScript 中如何区分开发和生产环境的 API 地址(支持 axios 请求

Vue3 TypeScript 中如何区分开发和生产环境的 API 地址&#xff08;支持 axios 请求&#xff09; 在实际项目开发中&#xff0c;我们通常会遇到以下需求&#xff1a; 本地开发时访问的是本地 API&#xff08;如 http://localhost:3000&#xff09;&#xff1b;上线打包后访问…

【数据结构】线性表之“双链表(带头循环双向链表)”

- 第 99 篇 - Date: 2025 - 05 - 25 Author: 郑龙浩/仟墨 【数据结构】 续上一篇: 线性表之“单链表” 文章目录 “双链表&#xff08;带头双向循环链表&#xff09;” 的实现:分步解释所有函数&#xff1a;test.cDListNode.hDListNode.c “双链表&#xff08;带头双向循环链表…

【学习笔记】Transformer

学习的博客&#xff08;在此致谢&#xff09;&#xff1a; 初识CV - Transformer模型详解&#xff08;图解最完整版&#xff09; 1 整体结构 Transformer由Encoder和Decoder组成&#xff0c;分别包含6个block。 Transformer的工作流程大体如下&#xff1a; 获取每个单词的em…

[MMU]IOMMU的主要职能及详细的验证方案

IOMMU的主要职能及详细的验证方案 摘要&#xff1a;IOMMU&#xff08;Input/Output Memory Management Unit&#xff09;是一种硬件组件&#xff0c;负责管理I/O设备对内存的直接访问&#xff08;DMA&#xff0c;Direct Memory Access&#xff09;&#xff0c;其主要作用是提供…

动物类 如何使用Yolov11训练使用牛羊数据集 实现对牛羊进行检测数据集

牛羊检测数据集 3700张 平视视角牛羊检测 带标注 voc yolo 牛羊检测数据集 3700张 牛羊检测平视 带标注 voc yolo 分类名: (图片张数&#xff0c;标注个数) cattle: (1395&#xff0c;4309) sheep: (2393&#xff0c;1 1205) 总数: (3791&#xff0c; 15514) 总类(nc): 2类 以…

搭建frp内网穿透

前言 内网穿透的原理我就不多说了哈&#xff0c;既然会看到我这篇文章&#xff0c;想必都知道内网穿透是做什么的吧 frp分为服务端和客户端&#xff0c;服务端一般是搭在公网服务器中&#xff0c;客户端一般搭在本地或者局域网&#xff0c;需要提前在服务端搭好ftp server&am…

Tailwind CSS 实战,基于 Kooboo 构建 AI 对话框页面(四):语音识别输入功能

基于前三章的内容&#xff0c;开发AI 对话框语音识别输入功能&#xff1a; Tailwind css实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;一&#xff09;-CSDN博客 Tailwind css实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;二&#xff09;&#xff1a;实…

ollama list模型列表获取 接口代码

ollama list模型列表获取 接口代码 curl http://localhost:11434/v1/modelscoding package hcx.ollama;/*** ClassName DockerOllamaList* Description TODO* Author dell* Date 2025/5/26 11:31* Version 1.0**/import java.io.BufferedReader; import java.io.InputStreamR…

ISOLAR软件生成报错处理(五)

错误1 An error has occurred. See error log for more details. java.lang.NullPointerException 这东西不用管&#xff0c;不影响生成 错误2 Description Resource Path Location Type Target ARObject: <xxxx> CompuMethod used for floating-point data conversi…

前端开发定时,ES学习,java集合

1.前端vue3加入定时任务&#xff1a; import { onMounted, ref,onUnmounted } from vue;//初始化&#xff0c;结束调用部分引用let timer: any;//定时器onMounted(async () > {timer setInterval(() > {open()//需要定时的任务}, 60000)//一分钟调用一次}); onUnmounte…

Photoshop2025(PS2025)软件及安装教程

在数字图像编辑领域&#xff0c;Adobe Photoshop 一直是无可争议的王者。如今&#xff0c;Photoshop 2025 重磅登场&#xff0c;再次为我们带来了惊喜与变革&#xff0c;进一步巩固了它在行业中的领先地位。 Photoshop 2025 在人工智能方面的升级令人瞩目。其全新的 “Magic Se…

【SQL Server Management Studio 连接时遇到的一个错误】

第一次用SQL Server Management Studio启动之后第一步就是要建立连接 但是不知道Server Name要填什么&#xff0c;看了网上的教程说是要找到下面这个注册表中对应的实例名称填上去&#xff0c;或者前面加localhost 但是好像都没有用&#xff0c;一直遇到报错如下&#xff1a;…

高等数学基础(向量矩阵及其创建和特殊的矩阵)

向量 向量是机器学习最底层的组成部分, 也是基础数据的表示形式, 线性代数通过将研究对象拓展到向量, 对多维数据进行统一研究, 而进化出的方法方便我们可以研究和解决真实世界中的问题 标量 标量也称为"无向量", 使用一个单独的数表示数值大小, 可以有正负之分, …