【JAVA 进阶】SpringMVC全面解析:从入门到实战的核心知识点梳理
📚 目录
- 一、SpringMVC概述与核心架构
- [1.1 什么是SpringMVC?](#1.1 什么是SpringMVC?)
- [1.2 SpringMVC核心架构图](#1.2 SpringMVC核心架构图)
- 二、SpringMVC核心组件详解
- [2.1 DispatcherServlet:前端控制器](#2.1 DispatcherServlet:前端控制器)
- [2.2 HandlerMapping:处理器映射器](#2.2 HandlerMapping:处理器映射器)
- [2.3 HandlerAdapter:处理器适配器](#2.3 HandlerAdapter:处理器适配器)
- [2.4 ViewResolver:视图解析器](#2.4 ViewResolver:视图解析器)
- 三、注解驱动开发
- [3.1 核心注解详解](#3.1 核心注解详解)
- [3.2 参数绑定注解](#3.2 参数绑定注解)
- 四、数据绑定与验证
- [4.1 表单数据绑定](#4.1 表单数据绑定)
- [4.2 数据验证](#4.2 数据验证)
- 五、拦截器与异常处理
- [5.1 拦截器(Interceptor)](#5.1 拦截器(Interceptor))
- [5.2 全局异常处理](#5.2 全局异常处理)
- 六、文件上传与下载
- [6.1 文件上传](#6.1 文件上传)
- [6.2 文件下载](#6.2 文件下载)
- [七、RESTful API开发实战](#七、RESTful API开发实战)
- [7.1 RESTful设计原则](#7.1 RESTful设计原则)
- [7.2 完整的RESTful控制器示例](#7.2 完整的RESTful控制器示例)
- 八、性能优化与最佳实践
- [8.1 性能优化建议](#8.1 性能优化建议)
- [8.2 最佳实践总结](#8.2 最佳实践总结)
- 九、常见问题与解决方案
- [9.1 中文乱码问题](#9.1 中文乱码问题)
- [9.2 静态资源访问](#9.2 静态资源访问)
- [9.3 跨域问题解决](#9.3 跨域问题解决)
- 十、学习资源与进阶方向
- [10.1 推荐学习资源](#10.1 推荐学习资源)
- [10.2 进阶方向建议](#10.2 进阶方向建议)
一、SpringMVC概述与核心架构
1.1 什么是SpringMVC?
SpringMVC是Spring Framework的一个模块,是基于MVC(Model-View-Controller)设计模式的轻量级Web框架。它通过DispatcherServlet作为前端控制器,将请求分发到相应的处理器,实现了请求处理流程的标准化和组件化。
核心特点:
- 松耦合设计:各组件职责明确,易于测试和维护
- 强大的注解支持:@Controller、@RequestMapping等注解简化开发
- 灵活的视图解析:支持JSP、Thymeleaf、FreeMarker等多种视图技术
- RESTful支持:天然支持REST风格的Web服务开发
- 与Spring生态无缝集成:可方便使用Spring的IoC、AOP、事务管理等特性
1.2 SpringMVC核心架构图
#mermaid-svg-J8gTygyAAiZ2JNnZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J8gTygyAAiZ2JNnZ .error-icon{fill:#552222;}#mermaid-svg-J8gTygyAAiZ2JNnZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J8gTygyAAiZ2JNnZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .marker.cross{stroke:#333333;}#mermaid-svg-J8gTygyAAiZ2JNnZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J8gTygyAAiZ2JNnZ p{margin:0;}#mermaid-svg-J8gTygyAAiZ2JNnZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster-label text{fill:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster-label span{color:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster-label span p{background-color:transparent;}#mermaid-svg-J8gTygyAAiZ2JNnZ .label text,#mermaid-svg-J8gTygyAAiZ2JNnZ span{fill:#333;color:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .node rect,#mermaid-svg-J8gTygyAAiZ2JNnZ .node circle,#mermaid-svg-J8gTygyAAiZ2JNnZ .node ellipse,#mermaid-svg-J8gTygyAAiZ2JNnZ .node polygon,#mermaid-svg-J8gTygyAAiZ2JNnZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .rough-node .label text,#mermaid-svg-J8gTygyAAiZ2JNnZ .node .label text,#mermaid-svg-J8gTygyAAiZ2JNnZ .image-shape .label,#mermaid-svg-J8gTygyAAiZ2JNnZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-J8gTygyAAiZ2JNnZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .rough-node .label,#mermaid-svg-J8gTygyAAiZ2JNnZ .node .label,#mermaid-svg-J8gTygyAAiZ2JNnZ .image-shape .label,#mermaid-svg-J8gTygyAAiZ2JNnZ .icon-shape .label{text-align:center;}#mermaid-svg-J8gTygyAAiZ2JNnZ .node.clickable{cursor:pointer;}#mermaid-svg-J8gTygyAAiZ2JNnZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .arrowheadPath{fill:#333333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J8gTygyAAiZ2JNnZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J8gTygyAAiZ2JNnZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J8gTygyAAiZ2JNnZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster text{fill:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ .cluster span{color:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-J8gTygyAAiZ2JNnZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J8gTygyAAiZ2JNnZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-J8gTygyAAiZ2JNnZ .icon-shape,#mermaid-svg-J8gTygyAAiZ2JNnZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J8gTygyAAiZ2JNnZ .icon-shape p,#mermaid-svg-J8gTygyAAiZ2JNnZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J8gTygyAAiZ2JNnZ .icon-shape .label rect,#mermaid-svg-J8gTygyAAiZ2JNnZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J8gTygyAAiZ2JNnZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J8gTygyAAiZ2JNnZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J8gTygyAAiZ2JNnZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Spring容器
客户端请求
DispatcherServlet
前端控制器
HandlerMapping
处理器映射器
Controller
处理器
ModelAndView
模型和视图
ViewResolver
视图解析器
View
视图
响应返回客户端
SpringMVC请求处理详细流程:
#mermaid-svg-se8OcqKew6BP93XK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-se8OcqKew6BP93XK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-se8OcqKew6BP93XK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-se8OcqKew6BP93XK .error-icon{fill:#552222;}#mermaid-svg-se8OcqKew6BP93XK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-se8OcqKew6BP93XK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-se8OcqKew6BP93XK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-se8OcqKew6BP93XK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-se8OcqKew6BP93XK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-se8OcqKew6BP93XK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-se8OcqKew6BP93XK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-se8OcqKew6BP93XK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-se8OcqKew6BP93XK .marker.cross{stroke:#333333;}#mermaid-svg-se8OcqKew6BP93XK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-se8OcqKew6BP93XK p{margin:0;}#mermaid-svg-se8OcqKew6BP93XK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-se8OcqKew6BP93XK .cluster-label text{fill:#333;}#mermaid-svg-se8OcqKew6BP93XK .cluster-label span{color:#333;}#mermaid-svg-se8OcqKew6BP93XK .cluster-label span p{background-color:transparent;}#mermaid-svg-se8OcqKew6BP93XK .label text,#mermaid-svg-se8OcqKew6BP93XK span{fill:#333;color:#333;}#mermaid-svg-se8OcqKew6BP93XK .node rect,#mermaid-svg-se8OcqKew6BP93XK .node circle,#mermaid-svg-se8OcqKew6BP93XK .node ellipse,#mermaid-svg-se8OcqKew6BP93XK .node polygon,#mermaid-svg-se8OcqKew6BP93XK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-se8OcqKew6BP93XK .rough-node .label text,#mermaid-svg-se8OcqKew6BP93XK .node .label text,#mermaid-svg-se8OcqKew6BP93XK .image-shape .label,#mermaid-svg-se8OcqKew6BP93XK .icon-shape .label{text-anchor:middle;}#mermaid-svg-se8OcqKew6BP93XK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-se8OcqKew6BP93XK .rough-node .label,#mermaid-svg-se8OcqKew6BP93XK .node .label,#mermaid-svg-se8OcqKew6BP93XK .image-shape .label,#mermaid-svg-se8OcqKew6BP93XK .icon-shape .label{text-align:center;}#mermaid-svg-se8OcqKew6BP93XK .node.clickable{cursor:pointer;}#mermaid-svg-se8OcqKew6BP93XK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-se8OcqKew6BP93XK .arrowheadPath{fill:#333333;}#mermaid-svg-se8OcqKew6BP93XK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-se8OcqKew6BP93XK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-se8OcqKew6BP93XK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-se8OcqKew6BP93XK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-se8OcqKew6BP93XK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-se8OcqKew6BP93XK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-se8OcqKew6BP93XK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-se8OcqKew6BP93XK .cluster text{fill:#333;}#mermaid-svg-se8OcqKew6BP93XK .cluster span{color:#333;}#mermaid-svg-se8OcqKew6BP93XK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-se8OcqKew6BP93XK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-se8OcqKew6BP93XK rect.text{fill:none;stroke-width:0;}#mermaid-svg-se8OcqKew6BP93XK .icon-shape,#mermaid-svg-se8OcqKew6BP93XK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-se8OcqKew6BP93XK .icon-shape p,#mermaid-svg-se8OcqKew6BP93XK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-se8OcqKew6BP93XK .icon-shape .label rect,#mermaid-svg-se8OcqKew6BP93XK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-se8OcqKew6BP93XK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-se8OcqKew6BP93XK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-se8OcqKew6BP93XK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 拦截器链
找到
未找到
适配成功
适配失败
解析成功
解析失败
客户端发送HTTP请求
DispatcherServlet接收请求
HandlerMapping查找处理器
返回HandlerExecutionChain
返回404错误
HandlerAdapter适配处理器
执行Controller方法
返回405错误
返回ModelAndView
ViewResolver解析视图
渲染视图并返回响应
返回500错误
preHandle()
Controller处理
postHandle()
afterCompletion()
二、SpringMVC核心组件详解
2.1 DispatcherServlet:前端控制器
DispatcherServlet是SpringMVC的核心,负责接收所有HTTP请求,并将请求分发给相应的处理器。
配置示例:
java
// web.xml配置
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2.2 HandlerMapping:处理器映射器
负责根据请求的URL找到对应的处理器(Controller)。
常用实现:
RequestMappingHandlerMapping:基于@RequestMapping注解BeanNameUrlHandlerMapping:基于Bean名称SimpleUrlHandlerMapping:基于URL模式
2.3 HandlerAdapter:处理器适配器
适配不同类型的处理器,使其能够被DispatcherServlet统一调用。
2.4 ViewResolver:视图解析器
将逻辑视图名解析为具体的视图对象。
配置示例:
xml
<!-- InternalResourceViewResolver配置 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
SpringMVC核心组件交互关系图:
#mermaid-svg-Fx9BNkh3FzvoIxCV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fx9BNkh3FzvoIxCV .error-icon{fill:#552222;}#mermaid-svg-Fx9BNkh3FzvoIxCV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fx9BNkh3FzvoIxCV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .marker.cross{stroke:#333333;}#mermaid-svg-Fx9BNkh3FzvoIxCV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fx9BNkh3FzvoIxCV p{margin:0;}#mermaid-svg-Fx9BNkh3FzvoIxCV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster-label text{fill:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster-label span{color:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster-label span p{background-color:transparent;}#mermaid-svg-Fx9BNkh3FzvoIxCV .label text,#mermaid-svg-Fx9BNkh3FzvoIxCV span{fill:#333;color:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .node rect,#mermaid-svg-Fx9BNkh3FzvoIxCV .node circle,#mermaid-svg-Fx9BNkh3FzvoIxCV .node ellipse,#mermaid-svg-Fx9BNkh3FzvoIxCV .node polygon,#mermaid-svg-Fx9BNkh3FzvoIxCV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .rough-node .label text,#mermaid-svg-Fx9BNkh3FzvoIxCV .node .label text,#mermaid-svg-Fx9BNkh3FzvoIxCV .image-shape .label,#mermaid-svg-Fx9BNkh3FzvoIxCV .icon-shape .label{text-anchor:middle;}#mermaid-svg-Fx9BNkh3FzvoIxCV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .rough-node .label,#mermaid-svg-Fx9BNkh3FzvoIxCV .node .label,#mermaid-svg-Fx9BNkh3FzvoIxCV .image-shape .label,#mermaid-svg-Fx9BNkh3FzvoIxCV .icon-shape .label{text-align:center;}#mermaid-svg-Fx9BNkh3FzvoIxCV .node.clickable{cursor:pointer;}#mermaid-svg-Fx9BNkh3FzvoIxCV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .arrowheadPath{fill:#333333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fx9BNkh3FzvoIxCV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Fx9BNkh3FzvoIxCV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fx9BNkh3FzvoIxCV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster text{fill:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV .cluster span{color:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Fx9BNkh3FzvoIxCV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Fx9BNkh3FzvoIxCV rect.text{fill:none;stroke-width:0;}#mermaid-svg-Fx9BNkh3FzvoIxCV .icon-shape,#mermaid-svg-Fx9BNkh3FzvoIxCV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fx9BNkh3FzvoIxCV .icon-shape p,#mermaid-svg-Fx9BNkh3FzvoIxCV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Fx9BNkh3FzvoIxCV .icon-shape .label rect,#mermaid-svg-Fx9BNkh3FzvoIxCV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fx9BNkh3FzvoIxCV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Fx9BNkh3FzvoIxCV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Fx9BNkh3FzvoIxCV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模型层
视图解析层
处理器层
处理器映射层
前端控制器层
返回JSON
DispatcherServlet
前端控制器
HandlerMapping
处理器映射器
HandlerAdapter
处理器适配器
@Controller
@RestController
其他处理器
ViewResolver
视图解析器
JSP视图
Thymeleaf视图
JSON视图
Model
数据模型
ModelAndView
模型和视图
三、注解驱动开发
3.1 核心注解详解
@Controller
java
@Controller
@RequestMapping("/user")
public class UserController {
// 控制器方法
}
@RequestMapping
java
@Controller
public class UserController {
// 处理GET请求
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listUsers(Model model) {
// 业务逻辑
return "user/list";
}
// 处理POST请求
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addUser(@ModelAttribute User user) {
// 业务逻辑
return "redirect:/user/list";
}
// 路径变量
@RequestMapping("/detail/{id}")
public String userDetail(@PathVariable("id") Long id, Model model) {
// 业务逻辑
return "user/detail";
}
}
@RestController(RESTful API专用)
java
@RestController
@RequestMapping("/api/users")
public class UserApiController {
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
3.2 参数绑定注解
@RequestParam
java
@GetMapping("/search")
public String searchUsers(@RequestParam("keyword") String keyword,
@RequestParam(value = "page", defaultValue = "1") int page) {
// 处理查询逻辑
return "user/list";
}
@PathVariable
java
@GetMapping("/{id}/orders")
public String getUserOrders(@PathVariable Long id,
@PathVariable("status") String orderStatus) {
// 获取用户订单
return "order/list";
}
@RequestBody和@ResponseBody
java
@PostMapping("/update")
@ResponseBody
public ApiResponse updateUser(@RequestBody UserUpdateRequest request) {
// 处理更新逻辑
return ApiResponse.success("更新成功");
}
SpringMVC注解使用决策流程图:
#mermaid-svg-yEmA2DIPaw0NBOIA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yEmA2DIPaw0NBOIA .error-icon{fill:#552222;}#mermaid-svg-yEmA2DIPaw0NBOIA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yEmA2DIPaw0NBOIA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yEmA2DIPaw0NBOIA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yEmA2DIPaw0NBOIA .marker.cross{stroke:#333333;}#mermaid-svg-yEmA2DIPaw0NBOIA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yEmA2DIPaw0NBOIA p{margin:0;}#mermaid-svg-yEmA2DIPaw0NBOIA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster-label text{fill:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster-label span{color:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster-label span p{background-color:transparent;}#mermaid-svg-yEmA2DIPaw0NBOIA .label text,#mermaid-svg-yEmA2DIPaw0NBOIA span{fill:#333;color:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA .node rect,#mermaid-svg-yEmA2DIPaw0NBOIA .node circle,#mermaid-svg-yEmA2DIPaw0NBOIA .node ellipse,#mermaid-svg-yEmA2DIPaw0NBOIA .node polygon,#mermaid-svg-yEmA2DIPaw0NBOIA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yEmA2DIPaw0NBOIA .rough-node .label text,#mermaid-svg-yEmA2DIPaw0NBOIA .node .label text,#mermaid-svg-yEmA2DIPaw0NBOIA .image-shape .label,#mermaid-svg-yEmA2DIPaw0NBOIA .icon-shape .label{text-anchor:middle;}#mermaid-svg-yEmA2DIPaw0NBOIA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yEmA2DIPaw0NBOIA .rough-node .label,#mermaid-svg-yEmA2DIPaw0NBOIA .node .label,#mermaid-svg-yEmA2DIPaw0NBOIA .image-shape .label,#mermaid-svg-yEmA2DIPaw0NBOIA .icon-shape .label{text-align:center;}#mermaid-svg-yEmA2DIPaw0NBOIA .node.clickable{cursor:pointer;}#mermaid-svg-yEmA2DIPaw0NBOIA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yEmA2DIPaw0NBOIA .arrowheadPath{fill:#333333;}#mermaid-svg-yEmA2DIPaw0NBOIA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yEmA2DIPaw0NBOIA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yEmA2DIPaw0NBOIA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEmA2DIPaw0NBOIA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yEmA2DIPaw0NBOIA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEmA2DIPaw0NBOIA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster text{fill:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA .cluster span{color:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yEmA2DIPaw0NBOIA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yEmA2DIPaw0NBOIA rect.text{fill:none;stroke-width:0;}#mermaid-svg-yEmA2DIPaw0NBOIA .icon-shape,#mermaid-svg-yEmA2DIPaw0NBOIA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEmA2DIPaw0NBOIA .icon-shape p,#mermaid-svg-yEmA2DIPaw0NBOIA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yEmA2DIPaw0NBOIA .icon-shape .label rect,#mermaid-svg-yEmA2DIPaw0NBOIA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEmA2DIPaw0NBOIA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yEmA2DIPaw0NBOIA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yEmA2DIPaw0NBOIA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 参数绑定
RESTful API
传统Web应用
获取资源
创建资源
更新资源
删除资源
页面跳转
表单提交
数据查询
开始处理HTTP请求
请求类型判断
使用@RestController
使用@Controller
方法注解选择
方法注解选择
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@RequestMapping
@PostMapping
@GetMapping
@RequestParam
查询参数
@PathVariable
路径参数
@RequestBody
请求体
@ModelAttribute
模型属性
4.2 数据验证
java
public class User {
@NotNull(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度3-20字符")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄必须大于18岁")
private Integer age;
// getters and setters
}
// 控制器中使用验证
@PostMapping("/register")
public String register(@Valid @ModelAttribute User user,
BindingResult result) {
if (result.hasErrors()) {
return "user/register";
}
userService.register(user);
return "redirect:/user/login";
}
数据验证处理流程图:
#mermaid-svg-y8yyfiHqzb9lkVYq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y8yyfiHqzb9lkVYq .error-icon{fill:#552222;}#mermaid-svg-y8yyfiHqzb9lkVYq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y8yyfiHqzb9lkVYq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y8yyfiHqzb9lkVYq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y8yyfiHqzb9lkVYq .marker.cross{stroke:#333333;}#mermaid-svg-y8yyfiHqzb9lkVYq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y8yyfiHqzb9lkVYq p{margin:0;}#mermaid-svg-y8yyfiHqzb9lkVYq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster-label text{fill:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster-label span{color:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster-label span p{background-color:transparent;}#mermaid-svg-y8yyfiHqzb9lkVYq .label text,#mermaid-svg-y8yyfiHqzb9lkVYq span{fill:#333;color:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq .node rect,#mermaid-svg-y8yyfiHqzb9lkVYq .node circle,#mermaid-svg-y8yyfiHqzb9lkVYq .node ellipse,#mermaid-svg-y8yyfiHqzb9lkVYq .node polygon,#mermaid-svg-y8yyfiHqzb9lkVYq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y8yyfiHqzb9lkVYq .rough-node .label text,#mermaid-svg-y8yyfiHqzb9lkVYq .node .label text,#mermaid-svg-y8yyfiHqzb9lkVYq .image-shape .label,#mermaid-svg-y8yyfiHqzb9lkVYq .icon-shape .label{text-anchor:middle;}#mermaid-svg-y8yyfiHqzb9lkVYq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y8yyfiHqzb9lkVYq .rough-node .label,#mermaid-svg-y8yyfiHqzb9lkVYq .node .label,#mermaid-svg-y8yyfiHqzb9lkVYq .image-shape .label,#mermaid-svg-y8yyfiHqzb9lkVYq .icon-shape .label{text-align:center;}#mermaid-svg-y8yyfiHqzb9lkVYq .node.clickable{cursor:pointer;}#mermaid-svg-y8yyfiHqzb9lkVYq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y8yyfiHqzb9lkVYq .arrowheadPath{fill:#333333;}#mermaid-svg-y8yyfiHqzb9lkVYq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y8yyfiHqzb9lkVYq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y8yyfiHqzb9lkVYq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y8yyfiHqzb9lkVYq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y8yyfiHqzb9lkVYq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y8yyfiHqzb9lkVYq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster text{fill:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq .cluster span{color:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y8yyfiHqzb9lkVYq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y8yyfiHqzb9lkVYq rect.text{fill:none;stroke-width:0;}#mermaid-svg-y8yyfiHqzb9lkVYq .icon-shape,#mermaid-svg-y8yyfiHqzb9lkVYq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y8yyfiHqzb9lkVYq .icon-shape p,#mermaid-svg-y8yyfiHqzb9lkVYq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y8yyfiHqzb9lkVYq .icon-shape .label rect,#mermaid-svg-y8yyfiHqzb9lkVYq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y8yyfiHqzb9lkVYq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y8yyfiHqzb9lkVYq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y8yyfiHqzb9lkVYq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 验证规则
通过
失败
用户提交表单
SpringMVC数据绑定
应用验证规则
验证结果
执行业务逻辑
收集错误信息
返回成功视图
返回表单视图
显示错误信息
@NotNull
非空验证
@Size
长度验证
@Email
邮箱验证
@Min/@Max
数值范围
自定义验证器
5.2 全局异常处理
java
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理业务异常
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(ex.getCode(), ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
// 处理所有未捕获异常
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex) {
ModelAndView mav = new ModelAndView();
mav.addObject("error", "系统异常,请稍后重试");
mav.addObject("message", ex.getMessage());
mav.setViewName("error/500");
return mav;
}
}
拦截器执行时序图:
View ViewResolver Handler 拦截器链 HandlerMapping DispatcherServlet 客户端 View ViewResolver Handler 拦截器链 HandlerMapping DispatcherServlet 客户端 #mermaid-svg-DjyE2wFGWLlsf2DN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DjyE2wFGWLlsf2DN .error-icon{fill:#552222;}#mermaid-svg-DjyE2wFGWLlsf2DN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DjyE2wFGWLlsf2DN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DjyE2wFGWLlsf2DN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DjyE2wFGWLlsf2DN .marker.cross{stroke:#333333;}#mermaid-svg-DjyE2wFGWLlsf2DN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DjyE2wFGWLlsf2DN p{margin:0;}#mermaid-svg-DjyE2wFGWLlsf2DN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DjyE2wFGWLlsf2DN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DjyE2wFGWLlsf2DN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-DjyE2wFGWLlsf2DN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-DjyE2wFGWLlsf2DN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-DjyE2wFGWLlsf2DN .sequenceNumber{fill:white;}#mermaid-svg-DjyE2wFGWLlsf2DN #sequencenumber{fill:#333;}#mermaid-svg-DjyE2wFGWLlsf2DN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-DjyE2wFGWLlsf2DN .messageText{fill:#333;stroke:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DjyE2wFGWLlsf2DN .labelText,#mermaid-svg-DjyE2wFGWLlsf2DN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .loopText,#mermaid-svg-DjyE2wFGWLlsf2DN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DjyE2wFGWLlsf2DN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-DjyE2wFGWLlsf2DN .noteText,#mermaid-svg-DjyE2wFGWLlsf2DN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-DjyE2wFGWLlsf2DN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DjyE2wFGWLlsf2DN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DjyE2wFGWLlsf2DN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DjyE2wFGWLlsf2DN .actorPopupMenu{position:absolute;}#mermaid-svg-DjyE2wFGWLlsf2DN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-DjyE2wFGWLlsf2DN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DjyE2wFGWLlsf2DN .actor-man circle,#mermaid-svg-DjyE2wFGWLlsf2DN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-DjyE2wFGWLlsf2DN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} break 返回false loop 每个拦截器 loop 每个拦截器(逆序) loop 每个拦截器 HTTP请求 查找处理器 HandlerExecutionChain preHandle() true/false 直接返回响应 执行处理器方法 返回ModelAndView postHandle() 解析视图 View对象 渲染视图 返回响应 afterCompletion()
6.2 文件下载
java
@GetMapping("/download/{filename:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
try {
Path filePath = Paths.get("uploads").resolve(filename).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
七、RESTful API开发实战
7.1 RESTful设计原则
- 使用HTTP方法表示操作类型(GET/POST/PUT/DELETE)
- 使用URI表示资源
- 使用HTTP状态码表示结果
- 使用JSON/XML作为数据交换格式
7.2 完整的RESTful控制器示例
java
@RestController
@RequestMapping("/api/products")
public class ProductApiController {
@Autowired
private ProductService productService;
// 获取产品列表
@GetMapping
public ResponseEntity<List<Product>> getAllProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<Product> products = productService.findAll(page, size);
return ResponseEntity.ok()
.header("X-Total-Count", String.valueOf(products.getTotalElements()))
.body(products.getContent());
}
// 获取单个产品
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// 创建产品
@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
Product savedProduct = productService.save(product);
return ResponseEntity.status(HttpStatus.CREATED)
.body(savedProduct);
}
// 更新产品
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable Long id,
@Valid @RequestBody Product product) {
if (!productService.existsById(id)) {
return ResponseEntity.notFound().build();
}
product.setId(id);
Product updatedProduct = productService.save(product);
return ResponseEntity.ok(updatedProduct);
}
// 删除产品
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
if (!productService.existsById(id)) {
return ResponseEntity.notFound().build();
}
productService.deleteById(id);
return ResponseEntity.noContent().build();
}
}
八、性能优化与最佳实践
8.1 性能优化建议
- 启用GZIP压缩
xml
<!-- web.xml配置 -->
<filter>
<filter-name>gzipFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>gzipFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 使用缓存
java
@GetMapping("/{id}")
@Cacheable(value = "products", key = "#id")
public Product getProduct(@PathVariable Long id) {
return productService.findById(id).orElseThrow();
}
- 异步处理
java
@GetMapping("/async")
@ResponseBody
public Callable<String> asyncProcessing() {
return () -> {
// 长时间运行的任务
Thread.sleep(3000);
return "异步处理完成";
};
}
8.2 最佳实践总结
- 遵循单一职责原则:每个控制器只处理相关资源的请求
- 合理使用注解:避免过度注解,保持代码简洁
- 统一异常处理:使用@ControllerAdvice统一处理异常
- 输入验证:始终验证用户输入,防止安全漏洞
- 日志记录:合理记录请求和异常信息
- API版本控制:为RESTful API设计版本策略
- 文档化:使用Swagger等工具生成API文档
九、常见问题与解决方案
9.1 中文乱码问题
xml
<!-- 在web.xml中配置字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
9.2 静态资源访问
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
9.3 跨域问题解决
java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
十、学习资源与进阶方向
10.1 推荐学习资源
- 官方文档:Spring Framework官方文档
- 书籍推荐:《Spring实战》、《Spring Boot编程思想》
- 在线课程:Spring官方培训课程、慕课网相关课程
- 社区资源:Spring官方论坛、Stack Overflow、GitHub开源项目
10.2 进阶方向建议
- Spring Boot整合:学习如何与Spring Boot快速集成
- 微服务架构:了解Spring Cloud与SpringMVC的结合
- 安全框架:深入学习Spring Security
- 性能监控:学习使用Spring Actuator进行应用监控
- 测试驱动开发:掌握SpringMVC单元测试和集成测试
总结:SpringMVC作为Java Web开发的核心框架,通过清晰的MVC架构、丰富的注解支持和强大的扩展能力,为开发者提供了高效、灵活的Web开发体验。掌握SpringMVC不仅能够提升开发效率,还能为后续学习Spring Boot、微服务等高级技术打下坚实基础。