承接上文Spring 异常处理之 HTTP 状态码,本文介绍spring异常处理的第二种方式。为什么说这种异常处理方式叫本地处理,因为该方式是在Controller内部处理该Controller中的方法抛出的异常。
需要注意的是,这种方式其实还可以细分为3种。具体见代码:
- 注意,下面的方法在同一个controller中
/**
* 异常处理之 ExceptionHandler 之一
*
* 这异常没有被 ResponseStatus 标注,必须被 handler 捕捉
*
* 本类的 conflict 方法捕获了它,对比下面一对
*
* @return 什么也不返回,总是抛出异常
*/
@RequestMapping("/dataIntegrityViolation")
public String throwDataIntegrityViolationException() {
throw new DataIntegrityViolationException("ID 重复");
}
/**
* 异常处理之使用 ExceptionHandler | 这个方法什么都不做,没什么实际应用价值
*
* 但是它可以为前端提供丰富的异常信息
*/
@ResponseStatus(value = HttpStatus.CONFLICT, reason = "数据冲突")
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// 什么也不做
}
/**
* 异常处理之 ExceptionHandler 之二
*
* 这异常没有被 ResponseStatus 标注,必须被 handler 捕捉
*
* 本类中的 databaseError 方法捕获了它,同时,该方法指定了 view,对比上面那一对
*
* @return 什么也不返回,总是抛出异常
* @throws SQLException 总是抛出这个异常
*/
@RequestMapping("/databaseError")
public String throwSqlException() throws SQLException {
throw new SQLException("SQL 异常");
}
/**
* 异常处理之使用 ExceptionHandler | 指定了 view
*
* 但是这种方式几乎不能给前端提供有效的异常信息
*
* @param exception 异常对象
* @return 视图名称
*/
@ExceptionHandler({SQLException.class})
public String databaseError(Exception exception) {
logger.info(exception.toString());
return "database.error";
}
/**
* 异常处理之 ExceptionHandler 之三
*
* 这异常没有被 ResponseStatus 标注,必须被 handler 捕捉
*
* 本类中的 handleError 方法捕获了它,同时,该方法指定了 model 和 view,对比上面那两对
*
* @return 什么也不返回,总是抛出异常
*/
@RequestMapping("/supportInfoException")
public String throwCustomException() {
throw new SupportInfoException("出错了");
}
/**
* 完全控制 - model 、view 、有用的异常信息
*
* @param req 当前的 HTTP 请求对象.
* @param exception 抛出的异常 - 也就是 {@link SupportInfoException}.
* @return 模型和视图
* @throws Exception 异常
*/
@ExceptionHandler(SupportInfoException.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) throws Exception {
if (AnnotationUtils.findAnnotation(exception.getClass(), ResponseStatus.class) != null)
throw exception;
logger.error("Request: " + req.getRequestURI() + " raised " + exception);
ModelAndView mav = new ModelAndView();
// 注意这里,这个 exception 是一个对象 | 而 DefaultErrorAttributes 中的 exception 是异常对象的完全限定名
mav.addObject("exception", exception);
mav.addObject("url", req.getRequestURL());
mav.addObject("timestamp", new Date().toString());
mav.addObject("status", 500);
mav.setViewName("support.error");
return mav;
}
// @formatter:off
//
// 注意:上面的三种异常处理方式的共同特点都是在 controller 内部使用 ExceptionHandler 来处理,我们可以将之称为"本地异常处理"
//
// @formatter:on
解释一下代码:我们在控制器的三个方法中抛出异常三个,然后使用对应的异常处理方法进行处理,这里的关键是@ExceptionHandler这个注解,正是它将异常捕获,然后在异常处理方法中进行处理。
- 从上面的代码中可以看出,第一种方式没有指定错误页面(使用默认的),而第二种和第三种方式都指定了错误页面,下面是三个异常页面
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Title</title>
<link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>
<h2>Spring Boot </h2>
<h3>
需要注意的是:在实际的开发中,绝对不应该向用户展示 Java 异常
</h3>
<!-- 下面这些关键字是在 org.springframework.boot.autoconfigure.web.DefaultErrorAttributes 中定义的 -->
<p th:if="${exception}">
<b>异常对象的类型:</b>
<span th:text="${exception}">exception type ...</span>
</p>
<p th:if="${timestamp}">
<b>发生时间:</b>
<span th:text="${timestamp}">timestamp ...</span>
</p>
<p th:if="${status}">
<b>响应状态码:</b>
<span th:text="${status}">status code ...</span>
<span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>
<p th:if="${message} and ${message.length() != 0}">
<b>错误原因:</b>
<span th:if="${message} and ${message.length() != 0}" th:text="${message}">message ...</span>
</p>
<p th:if="${path}">
<b>请求路径:</b>
<span th:text="${path}">path ...</span>
</p>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Title</title>
<link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>
<h2>DataBase 错误页面</h2>
<h3>
需要注意的是:在实际的开发中,绝对不应该向用户展示 Java 异常
</h3>
<h3>
该方式几乎不能为前端提供有效的异常信息
</h3>
<!-- 下面这些关键字是在 org.springframework.boot.autoconfigure.web.DefaultErrorAttributes 中定义的 -->
<p th:if="${exception}">
<b>异常对象的类型:</b>
<span th:text="${exception}">exception type ...</span>
</p>
<p th:if="${timestamp}">
<b>发生时间:</b>
<span th:text="${timestamp}">timestamp ...</span>
</p>
<p th:if="${status}">
<b>响应状态码:</b>
<span th:text="${status}">status code ...</span>
<span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>
<p th:if="${message} and ${message.length() != 0}">
<b>错误原因:</b>
<span th:if="${message} and ${message.length() != 0}" th:text="${message}">message ...</span>
</p>
<p th:if="${path}">
<b>请求路径:</b>
<span th:text="${path}">path ...</span>
</p>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Title</title>
<link th:href="@{webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>
</head>
<body>
<h1>Support Friendly Error Page</h1>
<!-- As we are using Thymeleaf, you might consider using
${#httpServletRequest.requestURL}. But that returns the path
to this error page. Hence we explicitly add the url to the
Model in some of the example code. -->
<p th:if="${url}">
<b>请求地址:</b> <span th:text="${url}">Page URL</span>
</p>
<!-- 自定义的,这里的 exception 是一个对象 -->
<p th:if="${exception}">
<b>错误消息:</b>
<span th:text="${exception.message}">exception message ...</span>
</p>
<p th:if="${timestamp}">
<b>发生时间:</b>
<span th:text="${timestamp}">Timestamp</span>
</p>
<p th:if="${status}">
<b>响应状态码:</b>
<span th:text="${status}">status-code</span>
<span th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>
</body>
</html>
-
最后,通过浏览器查看一下
-
参考