乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 SpringMVC概述与DispatcherServlet

    • 本章导读:Spring MVC概述与DispatcherServlet
    • Spring MVC 是什么
    • MVC 设计模式
    • 前端控制器模式
    • DispatcherServlet
    • 核心组件协作
  • 第2章 控制器与请求映射

    • 本章导读:控制器与请求映射
    • Controller
    • RestController
    • RequestMapping
    • GetMapping
    • PostMapping
    • PutMapping
    • DeleteMapping
    • PathVariable
    • RESTful
    • 请求映射原理
  • 第3章 请求参数获取与转换

    • 本章导读:请求参数获取与转换
    • RequestParam
    • RequestBody
    • RequestHeader
    • CookieValue
    • Model
    • ModelAttribute
    • 数据绑定原理
    • 数据校验
  • 第4章 响应数据与视图解析

    • 本章导读:响应数据与视图解析
    • ResponseBody
    • ResponseEntity
    • ModelAndView
    • ViewResolver
    • HttpMessageConverter
    • forward与redirect
  • 第5章 拦截器过滤器与跨域

    • 本章导读:拦截器、过滤器与跨域
    • HandlerInterceptor
    • WebMvcConfigurer
    • CrossOrigin
    • 登录验证实战
  • 第6章 文件上传与异常处理

    • 本章导读:文件上传与异常处理
    • MultipartFile
    • 文件下载
    • ExceptionHandler
    • ControllerAdvice
    • RestControllerAdvice
    • ResponseStatus
  • 第7章 高级特性与最佳实践

    • 本章导读:高级特性与最佳实践
    • SessionAttributes
    • SessionAttribute
    • RedirectAttributes
    • MockMvc测试
    • 国际化
    • 最佳实践
  • 第8章 扩展与异步机制

    • 本章导读:扩展与异步机制
    • 异步请求处理
    • 自定义参数解析器
    • 内容协商

ViewResolver

本章将视角从 RESTful API 转回传统服务器端渲染。在前后端分离成为主流之前,Spring MVC 的核心工作模式是"Controller 处理数据 → View 渲染页面 → 返回 HTML"。ViewResolver 就是这个链路中承上启下的关键组件——它把 Controller 返回的逻辑视图名(如 "employee-list")解析为物理视图路径(如 "/WEB-INF/views/employee-list.jsp"),让视图引擎找到正确的模板文件。


定义与作用

ViewResolver 是 Spring MVC 的视图解析器接口,职责是:根据 Controller 返回的逻辑视图名,查找并返回对应的 View 对象。

Controller 方法通常返回 String 类型的视图名:

@GetMapping("/employees")
public String list(Model model) {
    model.addAttribute("employees", empList);
    return "employee-list";  // 这是逻辑视图名,不是文件路径
}

"employee-list" 只是一个逻辑名称,真正的模板文件可能位于:

  • /WEB-INF/views/employee-list.jsp(JSP)
  • /templates/employee-list.html(Thymeleaf)
  • /WEB-INF/freemarker/employee-list.ftl(FreeMarker)

ViewResolver 负责把这个逻辑名映射到实际的物理位置,并创建对应的 View 实例。

生活类比:酒店房间预订

想象飞翔科技的行政人员为员工预订酒店:

  • Controller:行政人员说"订一间标准间"(返回逻辑视图名 "standard-room")
  • ViewResolver:预订系统根据"标准间"这个逻辑名称,查询到实际房间是"3 楼 305 号房"(解析为物理路径)
  • View:305 号房本身,包含床、电视、卫生间等设施(模板文件中的标签、样式、脚本)
  • 渲染:员工入住后,房间里的设施被实际使用(数据填充到模板,生成最终 HTML)

ViewResolver 就是那位"预订系统"——你只需说房间类型,它负责找到具体房号。


核心原理

视图解析过程

关键机制:

  1. 前缀后缀拼接:ViewResolver 通过 prefix 和 suffix 将逻辑名包装为完整路径
  2. 视图缓存:解析后的 View 对象通常会被缓存,避免重复查找文件
  3. 链式解析:Spring MVC 支持配置多个 ViewResolver,按顺序尝试,第一个成功即停止
  4. Locale 支持:部分 ViewResolver 支持国际化,根据用户语言解析不同语言的模板

适用位置与常用属性

ViewResolver 作为 Spring Bean,在 配置类 或 XML 配置 中声明。

常见实现对比

实现类模板技术典型前缀典型后缀适用场景
InternalResourceViewResolverJSP/WEB-INF/views/.jsp传统 JSP 项目
ThymeleafViewResolverThymeleaf/templates/.htmlSpring Boot 默认
FreeMarkerViewResolverFreeMarker/templates/.ftl高性能模板需求
UrlBasedViewResolver通用自定义自定义特殊路径规则

InternalResourceViewResolver 常用属性

属性说明示例
prefix视图文件路径前缀/WEB-INF/views/
suffix视图文件扩展名.jsp
viewClass视图实现类JstlView.class
cache是否缓存视图true(默认)
order解析器优先级数值越小优先级越高

完整示例

场景

飞翔科技员工管理系统使用 Thymeleaf 作为模板引擎。运维李眉部署时发现,所有视图文件放在 src/main/resources/templates/ 目录下。架构师白歌配置了 ThymeleafViewResolver,将 Controller 返回的逻辑名解析为 .html 文件。

代码实现

Spring Boot 自动配置(零配置):

Spring Boot 自动注册 ThymeleafViewResolver,默认配置:

  • 前缀:classpath:/templates/
  • 后缀:.html
// EmployeeController.java
package com.feixiang.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Arrays;
import java.util.List;

@Controller
@RequestMapping("/employees")
public class EmployeeController {

    @GetMapping
    public String list(Model model) {
        List<Employee> employees = Arrays.asList(
            new Employee("张三", "技术部", 25000),
            new Employee("李四", "产品部", 22000)
        );
        model.addAttribute("employees", employees);
        return "employee-list";  // 解析为 /templates/employee-list.html
    }

    @GetMapping("/detail")
    public String detail(Model model) {
        model.addAttribute("employee", new Employee("张三", "技术部", 25000));
        return "employee/detail";  // 解析为 /templates/employee/detail.html
    }
}

传统 Spring MVC 显式配置:

// WebConfig.java
package com.feixiang.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
public class WebConfig {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setCache(true);
        return resolver;
    }
}

前缀后缀拼接示例

逻辑视图名前缀后缀物理路径
employee-list/templates/.html/templates/employee-list.html
employee/detail/templates/.html/templates/employee/detail.html
admin/dashboard/WEB-INF/views/.jsp/WEB-INF/views/admin/dashboard.jsp
report/WEB-INF/freemarker/.ftl/WEB-INF/freemarker/report.ftl

HTTP 请求示例

示例 1:Thymeleaf 视图渲染

curl "http://localhost:8080/employees"

响应(Content-Type: text/html):

<!DOCTYPE html>
<html>
<head><title>员工列表</title></head>
<body>
    <h1>员工列表</h1>
    <table>
        <tr><th>姓名</th><th>部门</th><th>薪资</th></tr>
        <tr><td>张三</td><td>技术部</td><td>25000</td></tr>
        <tr><td>李四</td><td>产品部</td><td>22000</td></tr>
    </table>
</body>
</html>

示例 2:视图名包含子目录

curl "http://localhost:8080/employees/detail"

ViewResolver 将 "employee/detail" 解析为 /templates/employee/detail.html,支持多级目录结构。


易错场景与面试考点

误区一:视图文件放错位置

现象:Controller 返回 "employee-list",但报错 TemplateInputException: Error resolving template。

排查清单:

检查项ThymeleafJSP
文件位置src/main/resources/templates/employee-list.htmlsrc/main/webapp/WEB-INF/views/employee-list.jsp
前缀配置默认 /templates/需显式设置 /WEB-INF/views/
后缀配置默认 .html需显式设置 .jsp
缓存问题开发时关闭缓存:spring.thymeleaf.cache=false检查 cache 属性

误区二:@RestController 下配置 ViewResolver 无效

现象:@RestController 类中的方法返回视图名,但客户端收到纯文本。

纠正:@RestController 的所有方法默认带 @ResponseBody,返回值直接序列化,不经过 ViewResolver。需要视图渲染时,必须使用 @Controller。

误区三:多个 ViewResolver 顺序问题

现象:配置了 Thymeleaf 和 JSP 两个 ViewResolver,但总是用 JSP 解析,Thymeleaf 模板被忽略。

纠正:通过 order 属性控制优先级,数值越小越优先:

@Bean
public ViewResolver thymeleafViewResolver() {
    ThymeleafViewResolver resolver = new ThymeleafViewResolver();
    resolver.setTemplateEngine(templateEngine());
    resolver.setOrder(1);  // 优先尝试 Thymeleaf
    return resolver;
}

@Bean
public ViewResolver jspViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    resolver.setOrder(2);  // Thymeleaf 失败后再尝试 JSP
    return resolver;
}

面试高频:ViewResolver 的工作流程

标准回答:

  1. Controller 方法返回逻辑视图名(如 "employee-list")
  2. DispatcherServlet 将视图名和 Locale 传给 ViewResolver
  3. ViewResolver 通过 prefix + 视图名 + suffix 拼接物理路径
  4. 检查文件是否存在,存在则创建对应的 View 对象返回
  5. DispatcherServlet 调用 View.render(),传入 Model 数据
  6. 视图引擎读取模板文件,用 Model 数据替换变量,生成 HTML
  7. HTML 写入 HTTP 响应体返回给客户端

小结

ViewResolver 是 Spring MVC 视图渲染链路的"寻址系统"。它将 Controller 返回的抽象逻辑视图名,通过前缀后缀规则映射为具体的模板文件路径,让视图引擎能够找到并渲染正确的页面。

核心要点:

  • ViewResolver 只负责"找文件",不负责"渲染",渲染由 View 完成
  • 常见实现:InternalResourceViewResolver(JSP)、ThymeleafViewResolver(Thymeleaf)
  • 前缀 + 视图名 + 后缀 = 物理路径
  • 多个 ViewResolver 可通过 order 属性设置优先级
  • @RestController 不走视图解析,需要渲染时用 @Controller

本章与全局的关系:本章讲解了"视图名如何映射到模板文件"。下一节 ModelAndView 将讲解如何在 Controller 中同时控制视图名和数据,适合动态决定视图的场景。

上一页
ModelAndView
下一页
HttpMessageConverter