一、HttpMessageConverter
-
代表报文信息转化器,可以将请求报文转换为Java对象,也可以将Java对象转换为响应报文
-
请求报文分为三部分:请求头、请求空行和请求体
- post 请求才会生成请求体
- get 请求会把请求信息拼接到地址后
-
概述:这部分我们将介绍一些关于报文的注解和类型的使用
-
HttpMessageConverter 为我们提供了两个注解和两个类型:
- @RequestBody 用在控制器方法形参处,可以获取请求体
- @ResponseBody 用于控制器方法上,可以让方法的返回值直接响应到浏览器【在浏览器页面显示】
- RequestEntity 用于封装请求报文的一种类型,我们可以将其作为控制器方法的参数,封装传递过来的请求报文
- ResponseEntity 用于控制器方法的类型处,作用和@ResponseBody相同【ResponseEntity优先级更高】
-
接下来我们对各部分展开论述,并演示对应的用法
$ @RequestBody
-
用在控制器方法的形参前,我们一般用于字符串类型的变量接收请求体,是以键值对的形式存在的
-
编写我们的发起请求的表单,表单收集到的属性值和属性名就是我们请求体中的内容
<!--测试获得请求体--> <form th:action="@{/testRequestBody}" method="post"> <input type="text" name="username" > <input type="text" name="password" > <input type="submit" value="测试@RequestBody" > </form>
-
编写对应的控制器方法
// 获取请求体 @RequestMapping(value = "/testRequestBody", method = RequestMethod.POST) public String testRequestBody(@RequestBody String requestBody){ System.out.println(requestBody); return "success"; }
$ RequestEntity
-
类似于我们用User类来封装网页表单传过来的一个User对象的数据
-
RequestEntity 是一种请求报文的类型,作为形参使用 【我们得到的是当前的整个请求报文】
- 通过其对象的 getBody 方法可以获取请求体
- 通过其对象的 getHeaders 方法可以获取请求头
-
编写我们的请求表单:
<!--测试请求报文--> <form th:action="@{/testRequestEntity}" method="post"> <input type="text" name="username" > <input type="text" name="password" > <input type="submit" value="测试RequestEntity" > </form>
-
编写对应的控制器方法:
// 封装整个请求报文 @RequestMapping("/testRequestEntity") public String testRequestEntity(RequestEntity<String> requestEntity){ // 打印一下请求头和请求体 System.out.println(requestEntity.getHeaders()); System.out.println(requestEntity.getBody()); return "success"; }
$ @ResponseBody
- 使用该注解标记的控制器方法,返回的不再是一个视图名(用于页面跳转),而是直接作为响应报文的响应体响应到浏览器中
- 如果我们返回一个字符串,就会将这个字符串直接打印到浏览器页面
- 编写我们的请求链接:
<a th:href="@{/testResponseBody}">通过@ResponseBody响应浏览器数据</a><br>
- 编写我们的控制器方法 [测试的时候出现了乱码问题不大]
// 通过注解响应浏览器数据 @RequestMapping("/testResponseBody") @ResponseBody public String testResponseBody(){ return "通过注解响应到浏览器中的数据"; }
-
是否还有其他方式响应浏览器数据呢?
- 我们可以通过ServletAPI来完成
<a th:href="@{/testResponse}">通过ServletAPI响应浏览器数据</a><br>
@RequestMapping("/testResponse") public void testResponse(HttpServletResponse response) throws IOException { response.getWriter().println("hello, response"); }
- 我们还可以将 ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文【优先级比 @ResponseBody更高】
-
上面我们是将一个字符串作为响应报文的数据输出到浏览器页面,如果是一个我们封装的Java对象可以吗?
$ springMVC 处理 json
-
首先我们需要准备一个 pojo 类,此处我们以 User 类为例
package com.atguigu.mvc.bean; /** * @author Bonbons * @version 1.0 */ public class User { private Integer id; private String username; private String password; private Integer age; private String sex; public User() { } public User(Integer id, String username, String password, Integer age, String sex) { this.id = id; this.username = username; this.password = password; this.age = age; this.sex = sex; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; } }
-
准备一个发起请求的超链接
<a th:href="@{/testResponseUser}">通过@ResponseBody响应浏览器User对象</a><br>
-
编写控制器处理方法
// 测试返回的是一个对象 @RequestMapping("/testResponseUser") @ResponseBody public User testResponseUser(){ return new User(1, "LiHua", "10086", 28, "男"); }
-
运行 tomcat 测试一下
❌ 我们发现不能直接输出复杂类型,那么我们可以将其转化为 json 对象,然后进一步输出
-
那么我们应该怎么做才能将 Java 对象转换为 json 对象呢?
- 第一步,导入json相关依赖 【基于上面的条件我们已经完成了剩下的三步,只需要配置依赖】
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency>
- 第二步,在SpringMVC的核心配置文件中开启mvc注解驱动
- HandlerAdaptor会自动装配一个消息转换器,将响应到浏览器的Java对象转换为json格式的字符串
- 第三步,需要在控制器方法上使用 @ReponseBody 注解
- 第四步,将我们的 Java 对象作为返回值返回
-
重新运行,再次点击超链接测试【因为我使用了CSDN的插件】
-
json 是一种数据交互格式,分为 json 对象和 json 数组两种
- Json 对象外层是大括号
- Json 数组外层是中括号
-
对于 mvc 注解驱动有很多作用,当前我们接触到的有如下这些:
- 使用了视图控制器后控制器中的所有方法都会失效,我们需要配置注解驱动
- 在过滤静态资源的时候,我们如果不配置注解驱动就导致只有静态资源的请求才能被匹配到
- 用于Java对象转换成Json对象
-
SpringMVC 为我们提供了一个复合注解 @RestController 注解,用在控制器类名上
- 代表给类添加了 @Controller 注解
- 给类中所有的方法都添加了 @ReponseBody 注解
$ SpringMVC 处理 Ajax
- Ajax 本身就是页面,所以发送请求的方式不同,但是服务器的处理方式不变
- 页面不能刷新、也不与服务器进行交互,所以在服务器中不能转发和重定向,只能浏览数据【发起一个弹窗】
- 需求:通过一个div容器上的超链接的点击发起 ajax 请求,我们还是采用 vue 进行处理
编写我们的请求代码
<!--发送一个ajax请求-->
<div id="app">
<!--通过@click绑定点击事件-->
<a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理Ajax</a>
</div>
<!--引入我们的js文件-->
<script type="text/javascript" th:src="@{/static/js/vue.js}"/>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"/>
<script type="text/javascript">
new Vue({
el:"#app", // 对应我们连接的id
methods:{
testAxios:function (event){ // event 代表我们当前的事件
axios({
// 指定请求方式
method : "post",
// 请求地址
url : event.target.href,
// 请求参数
params: {
username : "admin",
password : "123456"
}
}).then(function (response){
alert(response.data);
})
// 取消超链接的默认行为
event.preventDefault();
}
}
});
</script>
编写我们的控制器方法
// Ajax 测试
@RequestMapping("/testAxios")
@ResponseBody
public String testAxios(String username, String password){
System.out.println(username + ", " + password);
return "hello, Ajax";
}
运行测试
二、文件的上传与下载
-
文件下载:从服务器端下载到浏览器端;文件上传:从浏览器端上传到我们服务器端 [在底层都是一个文件复制的过程]
-
在 SpringMVC 中,我们通过 ResponseEntity 作为控制器方法的返回值,来下载文件
- 属于用我们自定义的响应报文去响应浏览器,发起请求后就可以把对应的文件下载到浏览器
- 上面我们也说过,这个属性可以让控制器方法的返回值,作为响应报文响应到浏览器中
-
在 SpringMVC 中,我们想要上传文件需要配置对应依赖,表单发起post请求,编码方式得设置成二进制
- 上面的是前提,在控制器方法形参接收数据,但是要转换成MultipartFile,需要在核心配置文件中配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
有个小细节,springMVC 是根据 id 去查找Bean的,,我们声明的这个类是 MultipartResolver 接口的一个实现类
-
接下来演示如何实现在 Web 项目中进行文件的上传与下载:
$ 文件的下载
-
我们准备一个单独的页面,只用来发起文件上传和下载的请求,先通过一个超链接点击发起下载文件的请求
<a th:href="@{/testDown}">下载我们服务器端的1.png</a>
-
新建一个控制器类:FileUpAndDownController,提供两个控制器方法,先给出下载文件的方法
// 文件下载 @RequestMapping("/testDown") public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException { //获取ServletContext对象 [当前的整个工程] ServletContext servletContext = session.getServletContext(); //获取服务器中文件的真实路径 String realPath = servletContext.getRealPath("/static/img/1.png"); //创建输入流 InputStream is = new FileInputStream(realPath); //创建字节数组 [available方法是可以得到输入流中的字节数] byte[] bytes = new byte[is.available()]; //将流读到字节数组中 is.read(bytes); //创建HttpHeaders对象设置响应头信息 MultiValueMap<String, String> headers = new HttpHeaders(); //设置要下载方式以及下载文件的名字 [attachment代表以附件的方式下载] headers.add("Content-Disposition", "attachment;filename=1.png"); //设置响应状态码 [OK 代表200] HttpStatus statusCode = HttpStatus.OK; //创建ResponseEntity对象 [响应体、响应头、响应状态码] ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode); //关闭输入流 is.close(); return responseEntity; }
-
无论是文件上传还是下载,都要先获取到 Session,然后进一步获取到 ServletContext
-
既然要下载文件,肯定需要找到文件的位置,通过 getRealPath 方法将我们文件的路径传进去
-
我们要把文件中的内容都转化成字节发送到客户端,所以根据我们的路径创建输入流,然后将流读入字符数组中
-
因为我们传递响应报文不是只有响应体,还需要有响应头和响应状态码
- 所以直接通过 HttpHeaders创建一个响应头,属于一种Map集合,因为请求头和响应头都是键值对的形式
- 响应状态码 HttpStatus.OK 代表状态码200,就是成功响应,要是找不到资源就 404 了
-
最后将响应报文封装到 ResponseEntity 的对象中,通过 return 响应到浏览器中 【状态栏会有下载成功的提示】
-
测试一下,因为我使用特殊的下载工具,所以提示不一样
-
这部分代码可以当做模板直接使用,我们只需要修改一下文件名和请求提示就可以了
$ 文件的上传
-
对于文件上传,我们需要配置需要的依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
-
上传文件要求发起的必须是 post 请求,而且需要指定为二进制编码
<!--文件上传只能是post请求方式--> <!--enctype属性规定了表单数据发送到服务器之前如何进行编码--> <form th:action="@{/testUp}" method="post" enctype="multipart/form-data"> 头像 <input type="file" name="photo"><br> <input type="submit" value="上传"> </form>
-
在 SpringMVC 的核心配置文件中
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
-
接着我们看一下文件上传的控制器方法应该怎么写 ?
// 文件上传 @RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException { // 我们上传文件的文件名 String fileName = photo.getOriginalFilename(); // 获取文件名的后缀 String suffixName = fileName.substring(fileName.lastIndexOf(".")); // 上传之后的文件,我们使用UUID作为其文件名[通用唯一标识码,解决了文件重名覆盖问题] String uuid = UUID.randomUUID().toString().replace("-", ""); // 将uuid和后缀拼接到一起,就是服务器中的文件名 fileName = uuid + suffixName; // 通过session获取整个应用 ServletContext servletContext = session.getServletContext(); // 获取我们保存资源的路径 String photoPath = servletContext.getRealPath("photo"); // 获取整个路径下的文件 [主要我们用来判断是否有这个文件夹] File file = new File(photoPath); // 判断photoPath这个路径是否存在 if(!file.exists()){ // 不存在就去创建对应的文件夹 file.mkdir(); } // 创建我们最终的文件完整路径 String realPath = photoPath + File.separator + fileName; // 将浏览器中的资源转移到我们的服务器中 [会有一个IO流的异常,我们直接抛出] photo.transferTo(new File(realPath)); return "success"; }
-
返回值类型为String而且没使用@ResponseBody注解、返回值无前缀,所以我们返回的是视图名【成功上传跳转页面】
-
两个参数,第一个用来完成携带数据的封装,需要特定的配置(上面在核心文件中的配置),第二个获取 Session
-
此处我们给出的是直接解决了文件重名的问题,采用 UUID 代替我们上传的文件名,当然文件后缀不能变
-
需要注意,如果文件夹不存在我们需要创建一个文件夹,Maven工程文件实际保存到target目录下了
-
最后,将浏览器中的资源转移到我们指定的路径中
三、拦截器
-
什么是拦截器?
- 拦截器是一个运行在服务端的程序,先于与之相关的servlet或jsp页面之前运行,实现对请求资源的过滤功能
- SpringMVC 中拦截器用于拦截控制器方法的执行
-
过滤器也可以拦截请求,那么拦截器有什么特别的地方吗?
- 拦截器提供了三个方法
- preHandle 在控制器方法执行前执行,决定是否放行这个控制器方法,true通过、false就拒绝通过
- postHandle 在控制器方法执行结束之后执行
- AfterComplation 在渲染视图完成之后执行
- 描述一下大致流程:
- 在客户端发起请求,根据请求和我们的请求映射进行匹配,找到控制器方法【又称为处理器方法 Handle】
- 在控制器方法执行前,preHandle执行,然后执行控制器方法,然后执行postHandle
- 控制器的方法结束之后,都会返回一个ModelAndView的对象【模型和数据】
- postHandle执行结束后,将模型中的数据渲染到视图中,然后执行AfterComplation
- 拦截器提供了三个方法
-
接下来我们编写一个拦截器,演示拦截器具体是如何配置的?
$ 配置拦截器
☀️ 编写一个拦截器类 FirstInterceptor
- 我们可以让自定义拦截器类实现 HanlderInterceptor 接口,也可以让它继承 HandlerInterceptorAdapter
- 后者其实是前者的一个实现类,不过现在已经过时了,所以我们采用实现接口的方式
package com.atguigu.mvc.interceptors; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Bonbons * @version 1.0 */ @Component public class FirstInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("FirstInterceptor --> preHandle"); // return HandlerInterceptor.super.preHandle(request, response, handler); // 返回true代表放行,返回false代表拦截 // return false; return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("FirstInterceptor --> postHandle"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("FirstInterceptor --> afterCompletion"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
- 有几个需要注意的地方:
- 由于这个接口中的这三个方法定义为了 default 类型 【默认方法】
- 所以在我们实现这个接口的时候,不会报错提示必须实现这三个方法 ,在IDEA中我们可以按 Ctrl + O 弹出快速实现接口的选项窗口
☀️ 拦截器要想被我们SpringMVC识别到,需要进行配置
- 第一步,我们通过在拦截器类上使用 @Component 注解,将拦截器的Bean添加到IoC容器中管理【Bean是IoC中的一个组件/对象】
- 第二步,我们要确保这个文件能被扫描到,在核心配置文件中的context:component-scan 标签添加路径
- 第三步,在我们的核心配置文件中,配置拦截器
<!--配置拦截器--> <mvc:interceptors> <!--通过bean说明某一个类型的对象就是一个拦截器--> <bean class="com.atguigu.mvc.interceptors.FirstInterceptor"/> <ref bean="firstInterceptor"/> <mvc:interceptor> <!--代表对当前上下文路径上的请求进行拦截--> <!--如果想拦截所有请求,那么使用 /** --> <mvc:mapping path="/*"/> <!--除了我们的根标签请求(主页面)--> <mvc:exclude-mapping path="/"/> <!--通过bean或ref指定我们使用哪个拦截器--> <ref bean="firstInterceptor"/> </mvc:interceptor> </mvc:interceptors>
- 上面的这个配置文件不能直接使用,需要选择一种方案,注释掉另外两个
- 无法设置拦截规则,统一拦截所有请求
- 通过bean来声明拦截器,不需要指定 id,通过class属性指定为我们拦截器的全限定类名即可
- 通过ref来声明拦截器,因为这个拦截器已经托管了,只需要使用对应bean的默认id即可
- 可以设置拦截规则,拦截哪些请求也可以指定【也就是第三种方式 】
- 通过 mvc:interceptor 标签的子标签完成配置
- mvc:mapping 标签的 path属性指定拦截哪些请求
/*
代表当前上下文路径下的所有请求/**
代表所有请求
- mvc:exclude-mapping 标签的path属性指定不拦截哪些请求
- 接下来需要配置一下我们的拦截器,采用 bean 或 ref 的方式指定拦截器都可以
- mvc:mapping 标签的 path属性指定拦截哪些请求
- 通过 mvc:interceptor 标签的子标签完成配置
- 无法设置拦截规则,统一拦截所有请求
☀️ 配置好拦截器我们可以运行测试一下 【此时测试preHandler选择的是false >> 拦截方法】
- 我们可以看到浏览器页面什么都没有,三个方法也与我们预计的执行顺序相同
- 我们可以简单看一下各部分的调用位置 >> 决定了拦截器三个方法的执行顺序
$ 多拦截器的执行顺序
-
上面我们演示的是一个拦截器,如果存在多个拦截器,那么执行起来的效果是怎么样的呢?
-
首先,我们需要再准备一个拦截器 SecondInterceptor
package com.atguigu.mvc.interceptors; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Bonbons * @version 1.0 */ @Component public class SecondInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("SecondInterceptor --> preHandle"); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("SecondInterceptor --> postHandle"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("SecondInterceptor --> afterCompletion"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
-
接着在核心配置文件中配置一下拦截器,为了方便我们不设置拦截规则,采用 ref 声明拦截器
<mvc:interceptors> <ref bean="firstInterceptor"/> <ref bean="secondInterceptor" /> </mvc:interceptors>
-
运行测试一下:
-
我们对照源码,看一下为什么是这样的一个输出顺序?
- preHandle 是按照在配置文件中声明的顺序执行的
- postHandle 是按照在配置文件中声明的逆序执行的
- AfterComplation 也是按照在配置文件中声明的逆序执行的
- preHandle 是按照在配置文件中声明的顺序执行的
-
因为我们上面这两个拦截器都是放行状态,如果将第二个拦截器设置为拦截状态,那么会输出哪些内容呢?
-
实际上SpringMVC中还有一个默认的拦截器
-
存在多个拦截器,只要有一个拦截器的 preHandle 为 false,那么
- 这个返回 false 的拦截器和它之前的所有拦截器的 preHandle 会执行
- 所有拦截器的 postHandle 方法都不会执行
- 这个返回 false 的拦截器之前的所有拦截器 的 AfterComplation 方法会执行
四、异常处理器
-
异常处理器是SpringMVC中为我们提供的一种异常处理机制,我们可以自定义异常处理
-
主要分为两种方式进行配置:通过配置文件配置、通过注解配置
-
异常处理调用的是doResolveException方法,返回值为ModelAndView
- 当我们控制器方法出现了异常,这个方法执行后会产生一个新的ModelAndView代替原来的ModelAndView
- SimpleMappingExceptionResolver 类就是用来自定义异常的
-
捕获到我们预处理的异常后,就会根据我们的配置跳转到指定的页面,同时可以将错误信息添加到请求域中,在页面显示
$ 基于配置的异常处理
-
就是我们在SpringMVC的核心配置文件中配置异常处理解析器,来对配置我们如何处理异常
-
需要我们准备一个发生异常后的跳转页面,我们就输出一句话和打印异常报告
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 出现异常 <p th:text="${ex}"/> </body> </html>
-
设置一个发起异常测试请求的超链接
<a th:href="@{/testException}">测试异常处理</a>
-
我们模拟一个数学运算异常,在对应的控制器方法中添加异常
// 测试数学运算异常 @RequestMapping("/testException") public String testException(){ System.out.println(1/0); return "success"; }
-
在SpringMVC的核心配置文件中,配置异常解析器
<!--配置异常解析器--> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!--key为异常的全限定类名--> <prop key="java.lang.ArithmeticException">error</prop> </props> </property> <!--将异常信息添加到request请求域中--> <property name="exceptionAttribute" value="ex" /> </bean>
- 有几个需要注意的点:
- exceptionMappings 是 properties 类型,是一种Map集合,采用props嵌套prop标签设置键值对
- prop 标签的 key对应我们配置的异常的全限定类名,value 就是我们要跳转的页面
- 遵循我们视图跳转的规则,效果和return一样,需要看是否有前缀~
- 我们通过 exceptionAttribute 属性将异常信息以 value后参数值的名字作为key,添加到请求域中
- 有几个需要注意的点:
-
运行测试一下
$ 基于注解的异常处理
-
我们在额外定义一个控制器,专门完成各种异常处理 ExceptionController
-
在控制类上面,我们使用的不是 @Controller而是@ControllerAdvice注解,这个注解有三个作用
- .与@ExceptionHandler注解一起用,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的
- 与@InitBinder注解一起用,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的
- 与@ModelAttribute注解一起用,表示其注解的方法将会在目标Controller方法执行之前执行
-
编写我们的控制器和要测试的异常处理方法
package com.atguigu.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; /** * @author Bonbons * @version 1.0 */ @ControllerAdvice public class ExceptionController { @ExceptionHandler(value = { // 我们要处理的异常 ArithmeticException.class, NullPointerException.class }) /** * Exception ex 参数代表当前这个异常 * 使用Model model 形参为了将异常信息添加到请求域中 */ public String testException(Exception ex, Model model){ model.addAttribute("ex", ex); return "error"; } }
-
此时我们就可以直接进行测试了,我们在一个控制器方法模拟一下空指针异常
String s = null; s.length();
-
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口 HandlerExceptionResolver
-
HandlerExceptionResolver接口的实现类有:
- DefaultHandlerExceptionResolver 默认的异常处理器
- SimpleMappingExceptionResolver SpringMVC提供了自定义的异常处理器