SpringMVC
SpringMVC
SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架
SpringMVC通过一套注解,可以让普通的JAVA类成为contrllor控制器,无需继承Servlet
实现了控制层和Servlet之间的解耦。
SpringMVC支持Rest风格的URL写法
SpringMVC采用了松耦合,可热插的主键结构,比其他的框架更具扩展性和灵活性。
框架执行流程
1DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2 HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3 Handler:处理器 (自己定义的Controller处理单元)
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
4 HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
5 View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名 即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6 View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开 发具体的页面。
7 在配置里面写 <mvc:annotation-driven>
| 组件 | 作用 | 注解驱动下的实现类 |
|---|---|---|
| HandlerMapping(处理器映射器) | 根据 URL 找到对应的 Controller 方法 | RequestMappingHandlerMapping |
| HandlerAdapter(处理器适配器) | 调用目标方法,处理参数、返回值 | RequestMappingHandlerAdapter |
| ViewResolver(视图解析器) | 把逻辑视图名转成实际视图(如 JSP) | InternalResourceViewResolver |
🔔 注意:
<mvc:annotation-driven />只负责前两个(映射器 + 适配器),不包含视图解析器!所以你仍需手动配置<bean class="InternalResourceViewResolver">。
当配置了这个就相当于已经配置了前两个,然后框架就会自动拥有通过注解来获得
- 注册
RequestMappingHandlerMapping:扫描所有@Controller中的@RequestMapping,建立 URL → 方法的映射表 - 注册
RequestMappingHandlerAdapter:负责调用这些方法,自动处理:@RequestParam,@PathVariable,@RequestBody- 参数类型转换(如 String → Integer)
- 返回值处理(如
String→ 视图名,Object+@ResponseBody→ JSON)
1 | <!-- 1. 扫描 @Controller 等组件 --> |
@RequestMapping注解
1@RequestMapping控制请求方式
method属性可以控制请求的方式,值为RequestMethod的枚举值
1 | @RequestMapping( value = "/***" ,method = RequestMethod.GET) |
2@RequestMapping控制请求参数params和请求头headers
1 | param:表示请求中必须包含名为param的参数 |
3@PathVariable注解和RESTful风格的支持
普通形式的url
1 | *****/contextPath/aaa.do |
restFul风格的url
1 | *****/contextPath/aaa/10/root |
controller 处理单元
1 |
|
Http协议中,四个表示操作方式的动词”GET POST PUT DELETE”,他们对应四种基本操作,GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源
简单的说,就是我们在访问资源时,可以通过这四个状态来表示对于资源的不同操作,这四个状态表现为我们请求的四种方式
/controller/1 HTTP GET :得到id为1 的资源
/controller/1 HTTP DELETE :删除id为1的资源
/controller/1 HTTP PUT :更新id为1 的资源
/controller/1 HTTP POST :增加id为1 的资源
GET(获取资源)POST(提交数据)PUT(全量更新)PATCH(部分更新)DELETE(删除资源)HEAD、OPTIONS、TRACE等
在访问同一个url的时候,通过不同的请求方式,对应到不同的controller处理单元
但是HTML标准,只支持GET和POST,当然如果有可以直接发送比如Axios可以直接发送put . delete就不需要转换。
那么这时候就需要用一个过滤器来优先处理路径,也就是说请求过来会先经过过滤器,然后转换请求方法,再到DispatcherServlet。那么过滤器会拦截所有Post请求,然后检查是否有参数,如果有参数就会进行请求方法的转换。
Spring 提供了一个“变通方案”:
- 前端用 POST 表单提交
- 但在表单里加一个隐藏字段:
<input name="_method" value="DELETE"> - 后端通过
HiddenHttpMethodFilter识别这个字段,并把请求“伪装”成 DELETE
1 配置hiddenHttpMethodFilter
1 | <!--配置hiddenHttpMethodFilter ,将POST请求转换为PUT或者DELETE请求--> |
转换原理
1 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { |
Controller层代码
1 |
|
获取请求参数
在 Spring MVC 中:
DispatcherServlet接收请求- 通过
HandlerMapping找到对应的 Controller 方法 - 通过
HandlerAdapter(如RequestMappingHandlerAdapter)动态调用该方法 - 调用前,需要准备方法参数(比如从 request 中取值)
那么这个准备方法参数,就有两种方式
1、紧耦合方式
Controller 方法直接接收 HttpServletRequest 对象:
1 |
|
特点:
- 强依赖 Servlet API(
HttpServletRequest是 Java EE / Jakarta EE 的接口) - Controller 和 Web 容器 耦合紧密
- 单元测试困难(必须 mock
HttpServletRequest) - 代码可读性差(业务逻辑混杂了底层 API 调用)
💡 这就是所谓的“紧耦合”:你的业务代码直接绑定了 Web 层的具体实现。
2、解耦合方式
Controller 方法只声明业务需要的数据,不碰 HttpServletRequest:
1 |
|
或者更简洁(参数名一致时可省略 @RequestParam):
1 | public String handle(String username) { ... } // 要求请求参数名也是 "username" |
核心机制
“在单元方法上声明形参来接收请求数据时,形参名必须和请求数据的键名一致”
这句话的意思是:
- 当你写
public String handle(String username), - Spring MVC 会自动从 request 中查找名为
username的参数(即request.getParameter("username")) 然后把这个值注入给方法的
username形参这个过程叫 “参数解析”(Argument Resolution),由
HandlerMethodArgumentResolver体系完成。
⚠️ 注意:这只是默认行为。你可以用注解显式指定:
1 public String handle(@RequestParam("user_name") String username)这样即使形参叫
username,也会去取user_name参数。
使用POJO实体类来接受表单数据
前端提交一个包含多个字段的表单(姓名、年龄、爱好等),后端用一个 Java 对象(POJO)自动接收。
| 要求 | 说明 |
|---|---|
表单 name 属性 = POJO 属性名 |
如 <input name="pname"> → private String pname; |
| POJO 必须有 setter 方法 | Spring 通过反射调用 setPname() 赋值 |
| 支持数组/集合 | <input name="hobby"> 多个 → String[] hobby 或 List<String> |
接受日期类型
前端传的是字符串(如 "2025-11-24"),后端想直接得到 Date 或 LocalDate。
@DateTimeFormat(推荐)
1 |
|
接受List集合
1 | <input name="pets[0].petName"> |
1 | private List<Pet> pets; |
接受Map集合
1 | <input name="petMap['a'].petName"> |
1 | private Map<String, Pet> petMap; |
中文乱码问题
POST 用 Filter,GET 改服务器配置,这是经典组合。
POST 乱码:也是通过在过滤器层统一编码
1 | <filter> |
GET乱码:在server.xml中的Connector中更改Tomcat的配置
1 | <Connector port="8080" protocol="HTTP/1.1" |
常见注解
1、@RequestMapping
作用:用于建立请求 URL 和处理请求方法之间的对应关系
出现位置:
类上: 请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头
方法上: 请求 URL 的第二级访问目录
属性:
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params(了解):用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和 配置的一模一样。
headers(了解):用于指定限制请求消息头的条件。
2 @RequestParam
作用:把请求中指定名称的参数给控制器中的形参赋值。
属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
1 |
|
3、@PathVariable
Restful的简介 :
REST(英文:Representational State Transfer,简称 REST)restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
restful 的优点
它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
作用:
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。 url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
1 |
|
4、@RequestHeader(了解)
作用:
用于获取请求消息头。
属性:
value:提供消息头名称
required:是否必须有此消息头
1 |
|
5、@CookieValue(了解)
作用:
用于把指定 cookie 名称的值传入控制器方法参数。
属性:
value:指定 cookie 的名称。
required:是否必须有此 cookie
实现
1 |
|
响应处理
以前我们是自己在Servlet中使用response对象来完成响应的,那么在SpringMVC中如何响应请求的处理结果呢?
在SpringMVC中如果对于当前的控制单元,没有写对应的返回值,这个时候SpringMVC就会找和自己控制单元名称一致的页面展示,如果没有配置视图解析器的前缀和后缀是没有产生404,需要注意控制单元仍然可以进。
0单元方法返回值为void
1 |
|
1转发和重定向ServletAPI 实现
1 |
|
单元方法的返回值类型设置void。因为使用response对象在单元方法中直接对此次请求进行了响应,不再通过DispatcherServlet了,既然已经响应了,就不需要再给DispatcherServlet返回值了。在单元方法上声明HttpServletResponse形参,来接收此次请求的response对象。
2使用forward关键字完成响应
1 | /* |
使用通过单元方法的返回值来告诉DispatcherServlet请求转发指定的资源,如果是请求转发,forward关键字可以省略不写的
也就是说转发只是内部的转发共享同一个 request 和 response 对象,如果目标是另一个 Controller,仍然由同一个 DispatcherServlet 处理(不会重新进入 Servlet 容器的入口),而重定向是服务器返回一个 302 响应并在 Location 头中告诉浏览器:“请去 /target”,浏览器收到后,自动发起一个新的 GET 请求 到 /target,这个新请求 重新进入整个 Web 应用流程,回到了DispatcherServlet重新发送
3使用redirect关键字完成响应
1 | /* |
使用通过单元方法的返回值来告诉DispatcherServlet重定向指定的资源,注意这个redirect关键字不可以省去
4使用View视图转发和重定向
1 |
|
RedirectView中所做的操作,最终的实现是在renderMergedOutputModel中完成实现的,简单来说RedirectView实现了链接的重定向,并且将数据保存到FlashMap中,这样在跳转后的链接中可以获取一些数据.
5使用ModelAndView转发重定向
1 |
|
ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。
6ResponseBody 响应 json 数据
当浏览器发起一个ajax请求给服务器,服务器调用对应的单元方法处理ajax请求。而ajax的请求在被处理完成后,其处理结果需要直接响应。而目前我们在单元方 法中响应ajax请求,使用的是response对象,需要我们自己将要响应的数据转换 为json字符串响应,比较麻烦,而我们一直希望在单元方法中无论是否是ajax请求,都使用return语句来完成资源的响应,怎么办?
既然我们希望使用单元方法的返回值来响应ajax请求的处理结果,而目前DispatcherServlet的底层会将单元方法的返回值按照请求转发或者重定向来处理,所以就需要我们告诉DispatcherServlet,单元方法的返回值不要按照请求转发或者重定向处理,而是按照直接响应处理,将单元方法的返回值直接响应给浏览器。
那么看到这里会思考Controller方法的返回值是干什么的?
是告诉框架:
- 要跳转到哪个视图?
- 是转发(forward)还是重定向(redirect)?
- 要携带哪些数据(Model)给视图?
| 返回类型 | 示例 | 用途 |
|---|---|---|
String |
"user/profile" "redirect:/login" |
最常用,简洁 |
ModelAndView |
new ModelAndView("user/profile", "user", user) |
同时指定视图 + 模型数据 |
View |
new RedirectView("/login") |
直接返回视图对象(较少用) |
返回 String(最常见)
1 |
|
本质:返回的是逻辑视图名(logical view name)
Spring 会交给
ViewResolver如InternalResourceViewResolver)解析成实际路径1
2// 比如配置了 prefix="/WEB-INF/views/", suffix=".jsp"
// "result" → /WEB-INF/views/result.jsp
举个完整对比例子
假设要显示用户信息:
方式1:String + Model
1 |
|
方式2:ModelAndView
1 |
|
方式3:View(不推荐)
1 |
|
效果完全一样!但方式1最清晰、最主流。
那么Model是什么?
Model是后端(Controller)用来向前端(视图)传递数据的容器- 它是一个 Map
的封装(底层就是 ModelMap)
举个例子:
1 |
|
在 JSP 中:
1 | <h1>欢迎:${userInfo.name}</h1> <!-- 从 Model 中取数据 --> |
那么View是什么
View 只负责 “怎么渲染”,不负责“传什么数据”
View是一个接口,代表“渲染策略”
InternalResourceView→ 转发到 JSPRedirectView→ 重定向ThymeleafView→ 用 Thymeleaf 渲染
作用域传参
PageContext对象
作用域范围:当前jsp页面内有效
request对象
作用域范围:一次请求内。
作用: 解决了一次请求内的资源的数据共享问题
session对象
作用域范围:一次会话内有效。
说明:浏览器不关闭,并且后台的session不失效,在任意请求中都可以获取到同一个session对象。
作用:解决了一个用户不同请求的数据共享问题。
application(ServletContext)对象
作用域范围:整个项目内有效。
特点:一个项目只有一个,在服务器启动的时候即完成初始化创建无论如何获取都是同一个项目。
作用:解决了不同用户的数据共享问题。
| 概念 | 本质 | 存储位置 | 生命周期 | 使用场景 |
|---|---|---|---|---|
Model(Spring) |
Spring 封装的数据容器 | 最终存入 request 作用域 |
一次请求 | 向视图(JSP/Thymeleaf)传递数据 |
request |
Servlet 原生作用域 | HttpServletRequest |
一次请求 | 转发时共享数据 |
session |
Servlet 原生作用域 | HttpSession |
一次会话 | 登录用户信息、购物车 |
application |
Servlet 原生作用域 | ServletContext |
整个应用 | 全局配置、在线人数统计 |
PageContext |
JSP 特有作用域 | JSP 页面内部 | 一个 JSP 执行期间 | JSP 内部临时变量(很少用) |
上传文件
1如何在页面中显示一个按钮
用户可以点击该按钮后选择本地要上传的文件在页面中使用input标签,type值设置为”file”即可
2确定上传请求的发送方式
上传成功后的响应结果在当前页面显示,使用ajax请求来完成资源的发送
3上传请求的请求数据及其数据格式
请求数据:
上传的文件本身普通数据:用户名,Id,密码等,建议上传功能中不携带除上传资源以外的数据
数据格式:
传统的请求中,请求数据是以键值对的格式来发送给后台服务器的,但是在上传请求中,没有任何一个键可以描述上次的数据,因为数据本身是非常大的键就相当于一个变量,我们使用一个变量存储一个10g的电影显然是不可能 的。在上传请求中,将请求数据以二进制流的方式发送给服务器。
4在ajax中如何发送二进制流数据给服务器
① 创建FormData的对象,将请求数据存储到该对象中发送
② 将processData属性的值设置为false,告诉浏览器发送对象请求数据
③ 将contentType属性的值设置为false,设置请求数据的类型为二进制类型。
④ 正常发送ajax即可
5上传成功后后台服务器应该响应什么结果给浏览器
并且浏览器如何处理后台服务器处理完成后,响应一个json对象给浏览器,示例格式如下:
{ state:true,msg:“服务器繁忙”,url:”上传成功的资源的请求地址”}
6 文件上传依赖的jar
1 | <!--文件上传依赖--> |
7 配置文件上传组件
1 | <!--文件上传解析组件 |
前端代码
1 |
|
controller代码
1 |
|
文件上传中的几个问题
1 中文文件名编码问题:
已经通过过滤器解决
2 文件位置存储问题
放在当前项目下,作为静态资源,这样可以通过URL访问
1 |
|
在SpringMVC中配置静态资源放行
1 | <mvc:resources mapping="/upload/**" location="/upload/"></mvc:resources> |
3 文件名冲突问题
使用UUID对文件名进行重命名
| 对比维度 | UUID | 自增 ID(传统 ID) |
|---|---|---|
| 唯一性范围 | 全局唯一(跨机器、跨数据库) | 仅表内唯一 |
| 生成方式 | 本地生成(无需数据库) | 依赖数据库自增或序列 |
| 长度 | 36 字符(字符串)或 16 字节(二进制) | 通常 4~8 字节(int/bigint) |
| 可读性 | 差(一串乱码) | 好(1, 2, 3…) |
| 有序性 | ❌ 无序(v4 是随机的) | ✅ 严格递增 |
| 性能(数据库) | 插入慢(主键无序,B+树频繁分裂) | 插入快(顺序写入) |
| 安全性 | ✅ 不暴露业务信息 | ❌ 暴露数据量、增长速度 |
| 分布式支持 | ✅ 天然支持(各节点独立生成) | ❌ 需要分布式 ID 算法(如 Snowflake) |
1 |
|
4 控制文件类型
5 控制文件大小
1 |
|
6 上传图片回显问题
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
7 进度条问题
https://www.cnblogs.com/wuyu1787/p/8919588.html
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
8 单独准备文件存储服务器
分服务器上传作用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
应用服务器:负责部署我们的应用
在实际开发中,我们会有很多处理不同功能的服务器。(注意:此处说的不是服务器集群)
总结:分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
文件下载
1、下载的基本流程
文件的上传是将用户本地的资源发送到服务器,让服务器存储到其硬盘中的过程。而下载和上传正好是相反的过程。下载是用户发起请求,请求要下载的资源。服务器根据请求,将其硬盘中的文件资源发送给浏览器的过程。
2、下载的请求数据
用户通过浏览器发起下载请求,服务器在接收到请求后,根据当前请求的用户信息,去数据库中获取当前用户要下载的资源的文件路径,然后服务器再去其硬盘中读取对应的文件,将文件响应给浏览器,基于此过程,下载请求的请求数据为:简单的下载:文件的路径直接作为一个字段存储在用户信息表中用户的ID。
[1] 下载的后台实现
1. 创建单元方法处理下载请求
2. 根据请求获取要下载的资源的流对象
3. 读取文件并将资源响应给浏览器
4. 下载的示例代码
1 | 用户点击下载链接 |
1 |
|
拦截器
在之前学习JAVAWEB 的时候,我们学习了过滤器的知识。过滤器的作用是保护请求的服务器资源,在请求资源被执行之前,如果请求地址符合拦截范围,则会先执行过滤器。过滤器的执行时机,是在Servlet之前执行的。但是在使用了SpringMVC后,Servlet只有一个了,也就是DisptcherServlet。那么,如果我们仍然使用过滤器来完成请求的拦截,因为过滤器是在Servlet之前执行的,就会造成,过滤器会拦截DispatcherServlet所有的请求。那么,如果我们有部分请求不想被拦截,怎么办?
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。
1.通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2.通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
拦截器和过滤器的区别
1拦截器SpringMVC的,而过滤器是servlet的。
2拦截器不依赖与servlet容器,由spring容器初始化,过滤器依赖与servlet容器,由servlet容器初始化。
3拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6拦截器可以获取IOC容器中的各个bean,而过滤器就不太方便,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
定义一个拦截器
1 | public class MyInterceptor implements HandlerInterceptor { |
springmvc.xml中注册拦截器
1 | <!--注册拦截器--> |
拦截器内容详解
1、preHandle方法
执行时机
再进入控制单元方法之前执行
如何调用
按拦截器定义顺序调用
具体作用
如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去 进行处理,则返回 true。 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
参数详解
HttpServletRequest arg0,拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2 封存了单元方法对象的HandleMethod对象
1 | /** |
2、postHandle方法
执行时机
在进行数据处理和做出响应之间进行这个方法的调用
如何调用
在拦截器链内所有拦截器返成功调用
具体作用
在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,
在该方法中对用户请求 request域数据进行处理。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
ModelAndView arg3 封存了单元方法的返回值资源路径和请求转到的Map数据
1 | /** |
3、afterCompletion方法
执行时机
在进行页面渲染的时候执行
如何调用
按拦截器定义逆序调用
具体作用
在DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponsearg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
Exception arg3 存储了责任链的异常信息
1 | /** |
多个拦截器执行顺序
多个拦截器同时存在时,执行的顺序由配置顺序决定. 先配置谁, 谁就先执行.多个拦截器可以理解为拦截器栈, 先进后出(后进先出), 如图所示:
1 | <!--注册拦截器--> |
MyInterceptor preHandle
MyInterceptor2 preHandle
login.action
MyInterceptor2 postHandle
MyInterceptor postHandle
success.jsp
MyInterceptor2 afterCompletion
MyInterceptor afterCompletion
异常处理
SpringMVC异常简介
系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理,如下图
异常处理具体实现
1使用@ExceptionHandler注解处理异常
缺点:只能处理当前Controller中的异常。
1 |
|
2使用:@ControllerAdvice+@ExceptionHandler
此处优先级低于局部异常处理器
```java
package com.msb.exceptionhandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**- @Author: Ma HaiYang
- @Description: MircoMessage:Mark_7001
*/
@ControllerAdvice
public class GloableExceptionHandler1 {
@ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
public ModelAndView handelException(){
ModelAndView mv =new ModelAndView();
mv.setViewName(“error1.jsp”);
return mv;
}
}1
2
3
配置包扫描1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3使用:SimpleMappingExceptionResolver
xml配置
```xml
<!--自定义异常解析器-->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">redirect:/error.jsp</prop>
<prop key="java.lang.NullPointerException">redirect:/error2.jsp</prop>
</props>
</property>
</bean>
配置类配置
```java
/**- 全局异常
*/
@Bean@Configuration public class GloableException2 {
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
resolver.setExceptionMappings(prop);SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties prop = new Properties(); prop.put("java.lang.NullPointerException","error1.jsp"); prop.put("java.lang.ArithmeticException","error2.jsp");
return resolver;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4自定义的HandlerExceptionResolver
* ```java
/**
* 全局异常
* HandlerExceptionResolve
*/
@Configuration
public class GloableException3 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
if(e instanceof NullPointerException){
mv.setViewName("error1");
}
if(e instanceof ArithmeticException){
mv.setViewName("error2");
}
mv.addObject("msg",e);
return mv;
}}
- 全局异常
总结
当 Controller 中抛出异常时,Spring MVC 不会直接让异常“冒泡”到 Servlet 容器(那样会返回 500 错误页),而是:
- 捕获异常
- 交给异常解析器(
HandlerExceptionResolver)链处理 - 由解析器决定:返回错误页面?JSON?重定向?
💡 所有异常处理机制,底层都基于
HandlerExceptionResolver接口
方式1:@ExceptionHandler —— 控制器局部异常处理
作用:
- 只在当前 Controller 类内生效
- 捕获该 Controller 方法抛出的指定异常
- 不会继续向上抛出(即被“消费”掉了)
方式2:@ControllerAdvice + @ExceptionHandler —— 全局异常处理
作用:
- 对所有 Controller 生效
- 相当于把
@ExceptionHandler提取到一个公共类中 - 作用安慰,仅Controller层
方式3:SimpleMappingExceptionResolver —— 基于异常类名映射视图
作用:
- 传统 XML 配置时代常用的全局异常处理器
- 根据异常类型 → 映射到指定逻辑视图名
特点:
- 只能返回视图(JSP/Thymeleaf 页面)
- 不能返回 JSON 或自定义 HTTP 状态码
- 适用于纯服务端渲染项目
⚠️ 在 RESTful API(返回 JSON)场景下,基本不用它。
方式4:自定义 HandlerExceptionResolver
🔹 作用:
- 完全掌控异常处理逻辑
- 实现
HandlerExceptionResolver接口,加入 Spring 容器即可
执行顺序:
Spring 会按优先级调用多个 HandlerExceptionResolver,直到有一个返回 非 null 的 ModelAndView。
内置解析器顺序(可通过 setOrder() 调整):
1 | Controller 抛出异常 |
SpringMVC 其他注解
1、@PostMapping
作用:
指定当前发送请求的方式只可以是post请求
属性:
和@RequestMapping中属性一致
代码实现
1 | @PostMapping("/userControllerA") |
2、@GetMapping
作用:
指定当前发送请求的方式只可以是get请求
属性:
和@RequestMapping中属性一致
代码实现:
1 | @GetMapping("/userControllerA") |
3、@RestController
作用:
书写到类上,代表该类中所有控制单元方法均是ajax响应 相当于@ResponseBody+@Controller
属性:
其中的属性和@Controller中一样
代码实现:
1 | @RestController |
4、@JsonFormat
作用:
处理响应json 数据的处理
属性:
pattern :指定响应时间日期的格式
Timezone:指定响应的时区,否则会有8个小时的时间差
代码实现:
@DateTimeFormat(pattern = “yyyy-MM-dd”)
@JsonFormat(pattern = “yyyy-MM-dd” ,timezone=”GMT+8”)
private Date birth;
5、@RequestBody
作用:
用于获取请求体json格式的字符串内容。直接使用得到是 key=value&key=value…结构的数据,get 请求方式不适用。
属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值 为 false,get 请求得到是null。
实现:
1 | $(function () { |
1 |
|
6、@CrossOrigin
什么是跨域
出于浏览器的同源策略限制。同源策略(SameOriginPolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
http://127.0.0.1:8080/msb/index.jsp基础
https://127.0.0.1:8080/msb/index.jsp 协议不一样
http://192.168.24.11:8080/msb/index.jsp IP不一致
http://127.0.0.1:8888/msb/index.jsp 端口不一致
http://localhost:8080/msb/index.jsp IP不一致
作用:
解决ajax请求之间的跨域问题
属性:
origins : 允许可访问的域列表IP
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
代码实现:
1 |
|










