统一处理异常
服务端分为三层架构:表现层、业务层和数据层。
浏览器发送的请求一律发送给表现层,表现层调用业务层,业务层调用数据层(假如数据层出现异常,会抛给调用者业务层,业务层再往上抛会抛给表现层,就其实表现层是冲在最前方的,所以无论是数据层还是业务层的异常都会汇集到表现层)。
我们统一地对表现层捕获异常,就能处理所有的异常。
- SpringBoot提供了一些现成的方案,在项目的某一个特定的路径下加上对应特定错误状态的页面,项目在发现特定错误就会跳转到对应的页面(特定路径为:resources目录下的templates目录下的名为error的目录,且一定要命名为error,页面的名字一定是错误状态码)。
- 当出现服务端错误,我们可能会需要记录一些日志好进行相应的处理,Spring也有统一处理的方式。
@ControllerAdvice:用于修饰类,表示该类是Controller的全局配置类,在此类中,可以对Controller进行如下三种全局配置,即异常处理方案、绑定数据方案、绑定参数方案。
@ExceptionHandler:用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。
@ModelAttribute:用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。
@DataBinder:用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。
代码实现:
-
先写好一个错误的请求,Controller发生异常后,记录完日志之后需要重定向到错误页面:
// @RequestMapping(path = "/error",method = RequestMethod.GET) @GetMapping("/error") public String getErrorPage() { return "/error/500"; }
-
新建一个通知类ExceptionAdvice:
@ControllerAdvice(annotations = Controller.class) public class ExceptionAdvice { private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class); @ExceptionHandler({Exception.class}) // 方法名随便取,常用的参数为三个(Exception,HttpServletRequest,HttpServletResponse) // 当这个方法被调用就说明Controller肯定出现异常了 public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.error("服务器发生异常:" + e.getMessage()); // 栈的详细信息 for (StackTraceElement element : e.getStackTrace()) { logger.error(element.toString()); } // 记录完日志之后需要给浏览器一个响应,需要重定向到错误页面 String xRequestedWith = request.getHeader("x-requested-with"); if("XMLHttpRequest".equals(xRequestedWith)) { // 则为异步请求(此处采用向浏览器返回的是一个普通字符串,浏览器得到后需要人为的转为json格式) response.setContentType("application/plain;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(CommunityUtil.getJSONString(1,"服务器异常")); } else { // 则为普通请求 response.sendRedirect(request.getContextPath() + "/error"); } } }
使用这个组件的好处:
我们不需要对任何一个Controller做处理,不用在任何一个Controller上加代码就能解决问题。