异常处理重要性
- 良好的异常处理体系,便于对程序的后期维护
- 当发生错误时,程序不至于崩溃,提高程序健壮性
- 当发生错误时,可以在短时间内找定位问题所在
- 当发生错误时,避免异常栈裸奔,暴露底层架构,提高项目安全性
Spring统一异常方式
- 使用 @ ExceptionHandler 注解(缺点:异常处理的方法必须与出错的方法在同一个Controller里面,不能全局处理)
1 // 需要捕捉的异常
2 @ExceptionHandler({ BizException.class })
3 // Value用于设置response的状态码,例如404,200等,reason用于响应,可以是内容语句。
4 @ResponseStatus(code=HttpStatus.BAD_REQUEST,reason="bad request")
5 // 可以返回Json也可以进行跳转
6 @ResponseBody
7 public ServerResponse<?> exception(BizException e) {
8 return ServerResponse.createByErrorMessage(e.getMessage());
9 }
- 实现 HandlerExceptionResolver 接口
11 @Component
2 public class GlobalExceptionResolver implements HandlerExceptionResolver{
3
4 public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
5 //Object handler ----> HandlerMethod 也可能为null
6 // 可以通过handler进行特定处理
7 ..............
8 }
9}
- 使用@ControllerAdvice+ @ ExceptionHandler注解
1@ControllerAdvice
2@ResponseBody
3public class GlobalExceptionResolver {
4 @ResponseStatus(HttpStatus.BAD_REQUEST)
5 // 可以不指定特定的异常即默认拦截所有异常
6 @ExceptionHandler(HttpMessageNotReadableException.class)
7 public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
8 return ServerResponse.createByErrorMessage(e.getMessage())
9 }
10 // 其他代码省略
11}
SpringMVC异常处理源码剖析
- 从DispatcherServlet的doDispatch方法入手
1protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 try {
3 //省略请求处理代码部分
4 }catch (Exception ex) {
5 dispatchException = ex;
6 }
7 // 捕捉异常后调用processDispatchResult方法
8 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
9 }
10 finally {
11
12 }
13 }
- processDispatchResult如何处理呢
1private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
2 HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
3 // 先判断异常是否为null
4 if (exception != null) {
5 if (exception instanceof ModelAndViewDefiningException) {
6 logger.debug("ModelAndViewDefiningException encountered", exception);
7 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
8 }else {
9 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
10 mv = processHandlerException(request, response, handler, exception);
11 errorView = (mv != null);
12 }
13 }
14 }
- processHandlerException核心代码
1protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
2 Object handler, Exception ex) throws Exception {
3 // 省略了不关系部分(异常解析器排序)
4 ModelAndView exMv = null;
5 for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
6 // 调用resolveException方法
7 // Spring 自带的异常处理器暂时不讲本质上和我们自定义差不多
8 exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
9 if (exMv != null) {
10 // 只要获取ModeAndView终止处理
11 break;
12 }
13 }
14 // 如果没获得直接抛出异常
15 throw ex;
16 }
- Spring自带异常解析器主要接口和实现类
- AbstractHandlerMethodExceptionResolver和ExceptionHandlerExceptionResolver负责解析@ExceptionHandle
- ResponseStatusExceptionResolver解析@ResponseStatus
- DefaultHandlerExceptionResolver按照不同的类型分别对异常进行解析
- SimpleMappingExceptionResolver: 通过配置的异常类和view的对应关系来解析异常
拦截404
上面介绍的方法并不能拦截404,为什么要拦截404呢?首先为了产品的安全,不随便暴露后台所用的中间件,避免黑客利用中间件本身的漏洞攻击网站,另外也可以项目与用户有良好的交互。
利用Spring MVC的最精确匹配原则(@requestMapping(“*)拦截的这个方法返回一个自定义的404界面)
利用web容器提供的error-page
1<error-page>
2 // 也可以拦截其他错误码比如500
3 <error-code>404</error-code>
4 // 确保resource目录不被spring拦截
5 <location>/resource/view/404.htm</location>
6 </error-page>
- 重写DispatcherServlet的noHandlerFound方法
1protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception{
2 if(pageNotFoundLogger.isWarnEnabled())
3 pageNotFoundLogger.warn((new StringBuilder()).append("No mapping found for HTTP request with URI [").append(getRequestUri(request)).append("] in DispatcherServlet with name '").append(getServletName()).append("'").toString());
4 if(throwExceptionIfNoHandlerFound){
5 throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
6 } else{
7 //response.sendError(404);
8 // return;
9 // 修改为
10 // 定义一个请求路径为404的Controller方法
11 response.sendRedirect(request.getContextPath() + "/404");
12 }
13 }
项目中异常处理方案
1@Component
2public class GlobalExceptionResolver implements HandlerExceptionResolver {
3
4 public static final String AJAX="X-Requested-With";
5
6 public ModelAndView resolveException(HttpServletRequest request,
7 HttpServletResponse response, Object handler, Exception ex) {
8 // 区分是否是ajax请求
9 boolean isAjax = isAjax(request);
10 return handleException(request,response,handler,ex,isAjax);
11 }
12 /**
13 * 判断当前请求是否为异步请求.
14 * @param request
15 * @param response
16 * @return boolean
17 * @author zhaoxin
18 * @date 2018年9月18日 上午10:59:35
19 */
20 private boolean isAjax(HttpServletRequest request){
21 return StrUtil.isNotBlank(request.getHeader(AJAX));
22 }
23 /**
24 * 处理异常
25 * @return ModelAndView
26 * @author zhaoxin
27 * @date 2018年9月18日 上午11:52:40
28 */
29 private ModelAndView handleException(HttpServletRequest request,
30 HttpServletResponse response, Object handler,
31 Throwable ex, boolean isajax) {
32 //异常信息记录到日志文件中
33 LogUtil.logError("Capture global exceptions:"+ex.getMessage());
34 LogUtil.logException(ex);
35 //分普通请求和ajax请求分别处理
36 if(isajax){
37 return handleAjax(request,response,handler,ex);
38 }else{
39 return handlerNotAjax(request,response,handler,ex);
40 }
41 }
42
43 /**
44 * ajax异常处理并返回.
45 * @param request
46 * @param response
47 * @param handler
48 * @param initialEx
49 */
50 private ModelAndView handleAjax(HttpServletRequest request,
51 HttpServletResponse response, Object handler,Throwable initialEx){
52 response.setHeader("Cache-Control", "no-store");
53 // 返回JsonView
54 ModelAndView mav = new ModelAndView(new FastJsonJsonView());
55 mav.addObject("msg", "System exceptions please check logs");
56 mav.addObject("status", Const.RespondeCode.ERROR.getCode());
57 mav.addObject("success",false);
58 return mav;
59 }
60 /**
61 * 普通页面异常处理并返回.
62 * @param request
63 * @param response
64 * @param handler
65 * @param deepestException
66 * @return
67 */
68 private ModelAndView handlerNotAjax(HttpServletRequest request,HttpServletResponse response, Object handler, Throwable ex) {
69 Map<String, Object> model = new HashMap<>();
70 model.put("message", "System exceptions please check logs");
71 model.put("ex", ex);
72 return new ModelAndView("common/error500", model);
73 }
74
75
76}
求关注