携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
3.9 统一异常处理
SpringBoot中异常的自动处理
浏览器发送请求的时候一律发送给表现层,然后 表现层 调用 业务层,业务层 调用 数据层,假如 数据层 出异常了,它会抛给 业务层,然后 业务层 会抛给 表现层。所以整个三层架构,无论 数据层还是业务层哪个层次的异常,最终都会汇集到表现层,因此只要我们统一的对表现层捕获异常,就能处理系统的所有异常。
SpringBoot中异常处理
SpringBoot会自动处理异常,跳转到相应的 404.html 、500.html 对应的错误状态页面。
我们先将 404.html、500.html页面处理一下:
文件夹名字一定要叫 error,且在 templates 目录下,错误文件的名字一定要叫错误状态。
运行项目测试:
SpringBoot会自动给异常进行一个处理,但是这个处理还不是一定符合我们的预期,
比如 404 这个就ok,找不到资源没有更好的办法,500 就不一样,500 意味着服务端报错了,报错的时候我们最好记个日志,好以后分析,所以直接跳转页面这只是表面上的一个处理,我们内在需要记个日志这个还没有处理。
SpringBoot中异常的详细处理
接下来我们来看一下如何统一记录日志:
- @ModelAttribute
绑定数据的意思是:
比如 controller 有很多请求,这些请求当中要用到同一个数据给模板,我们需要往 model
里装同一个数据,我们可以利用 @ModelAttribute 加一个方法给 model 统一绑定参数,给所有的 controller 用。
-
@DataBinder
这个注解的作用是,页面向服务器传参,会被自动的做转换,是因为内部调了很多的参数转换器,万一程序当中默认的参数转换器不够用,有一个特殊的类型需要处理,可以自定义一个转换器,然后用 @DataBinder 把它注册上。
上面的 @ModelAttribute 和 @DataBinder 目前没有需求要用,所以就不演示了。接下来我们演示利用 @ExceptionHandler 统一处理所有 controller 可能发生的异常。
演示
首先在 HomeController 里加上一个请求,为什么呢?因为controller发生异常之后,我们统一处理,记了日志,处理完以后我们得去到 500那个页面,这个时候因为是我们人为处理的,我们需要手动的重定向过去,所以我们需要提前把 500 页面这个请求的访问给它配一下,增加一下这个请求的处理。
@RequestMapping(path = "/error", method = RequestMethod.GET)
public String getErrorPage() {
return "/error/500";
}
接下来我们需要用@ControllerAdvice 声明一个 controller 全局配置类,然后对所有 controller 的异常做统一的处理
@ControllerAdvice 不加参数的话会扫描所有的bean,范围太大了,我们通常需要一个限制,让它范围小一点,
@ControllerAdvice(annotations = Controller.class) 的意思是 这个注解只扫描带有 @Controller 注解的那些组件,
我们需要在这个类里面加一个方法处理所有的错误的情况,方法之前加一个注解@ExceptionHandler 表示这个方法是处理所有异常的方法
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {
// 为了记日志
private static Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
@ExceptionHandler({Exception.class}) // 里面的参数表示处理所有异常
public void handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { // 方法名无所谓
// 记录一下异常的概括
logger.error("服务器发生异常: " + e.getMessage());
// 详细记录异常的栈的信息
for (StackTraceElement element : e.getStackTrace()) {
logger.error(element.toString());
}
/*
普通的请求重定向到刚刚500的controller
异步请求的话返回json,json字符串里面写错误信息
*/
// 判断请求是普通请求还是异步请求
String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {
// 异步请求
response.setContentType("application/plain;charset=utf-8"); // plain表示向浏览器返回的是一个普通的字符串
PrintWriter writer = response.getWriter();
writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
} else {
// 普通请求重定向到错误页面
response.sendRedirect(request.getContextPath() + "/error");
}
}
}
上面的处理的话如果是 500 错误,表示服务器错误,会记个日志,然后跳到 500 那个controller,然后这个 controller会跳转到 500.html 界面
如果是 404 错误,也就是找不到资源,会直接跳转到 404.html 页面
测试
普通请求的 500 异常
异步发私信的 500 异常
到这里,统一异常处理就完成了。