异常映射

1、引言

项目在运行过程中有时会抛异常,对于 Web 应用来说,默认情况下异常信息会显示到页面上。开发的时候倒没什么,可是让用户看到就不好了。

SpringMVC 提供了一套机制将异常类型和一个视图对应起来。

因此我们需要捕获到这个异常,就去我们准备好的页面,让用户看到的是更加友好的错误页面。

……

2、基于 XML 配置异常映射

① 配置 Spring 配置文件 spring-mvc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 配置异常映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" id="exceptionResolver">
<!-- 配置具体的对应关系 -->
<property name="exceptionMappings">
<props>
<!-- key属性:指定异常类型 -->
<!-- 文本标签体:指定和异常对应的逻辑视图名称 -->

<!-- 匹配规则1:如果异常对象能够在映射关系中找到精确匹配的规则,那么就执行这个精确匹配的规则 -->
<!-- 匹配规则2:如果异常对象能够在映射关系中找到多个匹配的规则,优先采纳精确匹配的规则 -->
<!-- 匹配规则3:如果异常对象能够在映射关系中找到多个匹配的规则,且没有精确匹配的,那么会采纳范围更接近的那个 -->
<!-- 匹配规则4:如果注解中也配置了相同的映射关系,那么SpringMVC会优先采纳基于注解的映射 -->
<prop key="java.lang.ArithmeticException">show-message</prop>
<prop key="java.lang.RuntimeException">show-runtime-message</prop>
<prop key="java.lang.Exception">show-exception-message</prop>

</props>
</property>

<!-- 显示异常信息 -->
<!-- exceptionAttribute属性:设置将异常对象存入请求域时使用的属性名 -->
<!-- 如果没有配置这个属性,那么默认使用"exception"作为属性名 -->
<!--<property name="exceptionAttribute" value="testException"/>-->
<!--
源码中文档说明如下:
* Set the name of the model attribute as which the exception should be exposed.
* Default is "exception".
-->
<property name="exceptionAttribute" value="testException"/>
</bean>

② 测试:编写一个异常对应的 jsp 页面

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>系统信息</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/index.jsp">回首页</a></br>

<!-- 如果没有配置exceptionAttribute属性,那么默认使用"exception"作为属性名 -->
异常对象:${requestScope.testException}<br>
异常消息: ${requestScope.testException.message}<br>
</body>
</html>

3、基于注解配置异常映射

注意:如果 XML 中也配置了相同的映射关系,那么 SpringMVC 会优先采纳基于注解的映射

① 方法一

创建一个类,使用@ControllerAdvice 标记

然后使用标记了@ExceptionHandler 注解的方法进行异常映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 这个注解表示当前类是一个异常映射类
@ControllerAdvice
public class MyExceptionResolver {

// @ExceptionHandler注解中指定异常类型
// 如果XML中也配置了相同的映射关系,那么SpringMVC会优先采纳基于注解的映射
@ExceptionHandler(value = Exception.class)
public ModelAndView exceptionMapping(

// 方法形参位置接收SpringMVC捕获到的异常对象
Exception exception
) {

// 在处理异常的过程中,我们可以将异常对象存入模型;将展示异常信息的视图设置为逻辑视图名称
ModelAndView modelAndView = new ModelAndView();

// 放入捕获到的异常对象exception,"exceptionClass", 对应jsp展示页面上的属性名
// 异常对象:${requestScope.exceptionClass}
modelAndView.addObject("exceptionClass", exception);

//"show-annotation-message"为对应的jsp展示页面,show-annotation-message.jsp
modelAndView.setViewName("show-annotation-message");

return modelAndView;
}

② 方法二

1
2
3
4
5
6
7
8
9
10
11
@ControllerAdvice
public class MyExceptionResolver {
//value属性:异常类型
//无返回值,由response负责给出响应
@ExceptionHandler(value=Exception.class)
public void exceptionResolver(Exception exception, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("使用HttpServletResponse对象返回的响应数据!!!");
}
}

结果:如果有异常,会在对应的请求页面显示【使用 HttpServletResponse 对象返回的响应数据!!!】

注意这里为什么不返回视图了呢?(没有 return ”xxxxx”;)

因为如果浏览器发送的是一个 Ajax 请求,那么返回视图是处理不了的,需要返回 JavaScript 程序能够处理的数据片段。

[1]判断是否为 Ajax 请求依据

1
2
// 两个条件满足任何一个都能够确定当前请求是Ajax请求
if (accept.contains("application/json") || xRequestedWith != null)

[2]同时适配普通请求和 Ajax 请求的异常处理

需要额外导入 jar 包

gson-2.2.4

jackson-all-1.9.11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@ExceptionHandler(value = Exception.class)
public ModelAndView resolveException(
Exception exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

// 判断是不是Ajax请求,需要读取请求消息头
// 通过request对象获取请求消息头
String accept = request.getHeader("Accept");
String xRequestedWith = request.getHeader("X-Requested-With");

// 两个条件满足任何一个都能够确定当前请求是Ajax请求
if (accept.contains("application/json") || xRequestedWith != null) {

// 对于Ajax请求不能返回页面作为响应结果,而应该返回数据片段(JSON格式或文本格式)
Map<String,String> exceptionInfomationMap = new HashMap<>();
// 异常对象名称
exceptionInfomationMap.put("exceptionClass", exception.getClass().getName());
// 异常消息
exceptionInfomationMap.put("exceptionMessage", exception.getMessage());

Gson gson = new Gson();
String json = gson.toJson(exceptionInfomationMap);

response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(json);

// 此时不需要提供ModelAndView,所以返回null
return null;
}

// 如果不是Ajax请求,那么就正常返回ModelAndView对象
ModelAndView modelAndView = new ModelAndView();

modelAndView.addObject("exception", exception);

modelAndView.setViewName("show-annotation-message");

return modelAndView;
}