一、前后端分离 vs 传统MVC模式对比
1.1 数据流向差异
graph TD
subgraph 前后端分离
A[浏览器] --> B[前端服务器] -->|Ajax| C[Spring API]
C -->|JSON| B -->|组装页面| A
end
subgraph 传统MVC
D[浏览器] --> E[Spring MVC]
E -->|处理请求| F[模板引擎]
F -->|HTML| D
end
1.2 核心组件差异
| 组件 | 前后端分离 | 传统MVC |
|---|---|---|
| 返回类型 | @RestController | @Controller |
| 视图解析器 | 无 | ViewResolver |
| 数据载体 | ResponseEntity | Model |
| 响应内容 | JSON/XML | HTML |
二、传统MVC请求全流程(重点视图阶段)
2.1 完整处理流程
sequenceDiagram
participant Browser
participant DispatcherServlet
participant HandlerMapping
participant HandlerAdapter
participant Controller
participant ViewResolver
participant View
Browser->>DispatcherServlet: 1. 发起请求
DispatcherServlet->>HandlerMapping: 2. 查找Handler
HandlerMapping-->>DispatcherServlet: 返回HandlerChain
DispatcherServlet->>HandlerAdapter: 3. 适配执行
HandlerAdapter->>Controller: 4. 调用控制器方法
Controller->>HandlerAdapter: 5. 返回ModelAndView
HandlerAdapter-->>DispatcherServlet: 返回ModelAndView
DispatcherServlet->>ViewResolver: 6. 解析视图名称
ViewResolver-->>DispatcherServlet: 返回View对象
DispatcherServlet->>View: 7. 渲染视图
View-->>DispatcherServlet: 生成HTML
DispatcherServlet-->>Browser: 8. 返回响应
2.2 关键阶段详解(视图相关)
阶段6:视图解析(ViewResolver)
// 伪代码:DispatcherServlet中处理视图解析
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 解析视图名称
View view;
if (mv.isReference()) {
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale);
}
// 渲染视图
view.render(mv.getModelInternal(), request, response);
}
阶段7:视图渲染(View)
// 以JSP视图为例
public class InternalResourceView extends AbstractUrlBasedView {
protected void renderMergedOutputModel(
Map<String, Object> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 将模型数据暴露为请求属性
exposeModelAsRequestAttributes(model, request);
// 转发到JSP页面
RequestDispatcher rd = request.getRequestDispatcher(getUrl());
rd.forward(request, response);
}
}
三、代码实战:传统MVC开发模式
3.1 控制器写法对比
// 前后端分离写法
@RestController
public class ProductApiController {
@GetMapping("/api/products")
public List<Product> listProducts() {
return productService.findAll();
}
}
// 传统MVC写法
@Controller
public class ProductViewController {
@GetMapping("/products")
public String listProducts(Model model) {
model.addAttribute("products", productService.findAll());
return "product/list"; // 视图名称
}
}
3.2 视图层三要素配置
3.2.1 视图解析器配置(JSP示例)
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
3.2.2 JSP页面示例
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>产品列表</title>
</head>
<body>
<h1>所有产品</h1>
<table>
<tr>
<th>ID</th>
<th>名称</th>
<th>价格</th>
</tr>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.id}</td>
<td>${product.name}</td>
<td>¥<fmt:formatNumber value="${product.price}" pattern="#,##0.00"/></td>
</tr>
</c:forEach>
</table>
</body>
</html>
3.2.3 文件结构
src/main/webapp
└── WEB-INF
└── views
└── product
└── list.jsp
四、核心机制深度解析
4.1 模型数据传递原理
classDiagram
class Model {
<<interface>>
+addAttribute(String name, Object value) Model
+addAttribute(Object value) Model
+mergeAttributes(Map<String, ?> attributes) Model
+asMap() Map<String,Object>
}
class BindingAwareModelMap {
-target: Map<String, Object>
+addAttribute(String name, Object value)
+getAttribute(String name) Object
}
Model <|.. BindingAwareModelMap
4.2 视图解析器链
// DispatcherServlet中的视图解析逻辑
protected View resolveViewName(String viewName, Map<String, Object> model,
Locale locale, HttpServletRequest request) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
4.3 视图渲染过程
- 模型数据转换:将Model中的属性设置到Request作用域
- 视图定位:根据视图解析器的前缀/后缀找到物理文件
- 模板处理:执行JSP编译或Thymeleaf渲染
- 输出响应:将生成的HTML写入HttpServletResponse
五、高级视图技术集成
5.1 Thymeleaf整合示例
@Configuration
public class ThymeleafConfig {
@Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new Java8TimeDialect());
return engine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
5.2 复杂数据展示
<!-- Thymeleaf模板示例 -->
<table>
<tr th:each="prod : ${products}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">默认产品名</td>
<td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.00</td>
<td>
<span th:switch="${prod.status}">
<span th:case="0" class="text-muted">已下架</span>
<span th:case="1" class="text-success">在售</span>
</span>
</td>
</tr>
</table>
六、常见问题解决方案
6.1 视图找不到(404错误)
排查步骤:
- 检查视图解析器的前缀/后缀配置
- 确认返回的视图名称是否匹配模板文件名
- 查看控制台是否有模板编译错误
6.2 模型数据未显示
解决方案:
// 确保使用Model.addAttribute()
@GetMapping("/detail")
public String productDetail(Model model) {
Product product = productService.findById(1L);
model.addAttribute("product", product); // 正确
// model.addAttribute(product); // 自动根据类型命名(首字母小写)
return "product/detail";
}
6.3 静态资源加载失败
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
七、从理论到实践:完整案例
7.1 表单提交处理
@Controller
public class ProductController {
@GetMapping("/products/new")
public String showForm(Model model) {
model.addAttribute("product", new Product());
return "product/form";
}
@PostMapping("/products")
public String createProduct(@ModelAttribute Product product,
BindingResult result) {
if (result.hasErrors()) {
return "product/form";
}
productService.save(product);
return "redirect:/products";
}
}
7.2 表单页面(Thymeleaf)
<form th:action="@{/products}" method="post" th:object="${product}">
<div>
<label>产品名称:</label>
<input type="text" th:field="*{name}">
<span th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">名称错误提示</span>
</div>
<div>
<label>价格:</label>
<input type="number" step="0.01" th:field="*{price}">
</div>
<button type="submit">提交</button>
</form>
总结:视图层的核心价值
通过传统MVC模式开发,可以实现:
- 快速原型开发:前后端协同在同一个工程中
- SEO友好:服务端渲染HTML对搜索引擎更友好
- 渐进增强:可逐步引入前端框架
- 完整功能支持:表单处理、验证、国际化等开箱即用
理解视图层的工作原理,是掌握传统Web开发模式的关键。建议从简单JSP开始实践,再逐步过渡到Thymeleaf等现代模板引擎,最终形成完整的MVC开发能力。