后端发生异常的时候,如果不处理,返回前段的会是printStackTrace()打出来的结果,会泄露我们服务器代码信息,可能会被利用攻击,所以需要一个统一的异常处理机制。
解决方案
@ExceptionHandler 和@ControllerAdvice 注解,在一个文件中处理所有异常类型,并接口返回固定格式。
@ControllerAdvice
public class WebExceptionHandle {
@ExceptionHandler(DataAccessException.class)
@ResponseBody
ResultDTO handleSqlException(Exception e){
// 处理sql异常
return ResultDTO.faileResult(null,"sql failed");
}
@ExceptionHandler()
@ResponseBody
ResultDTO handleException(Exception e){
//默认异常处理
return ResultDTO.faileResult(null,"sql failed");
}
}
博文:https://blog.csdn.net/liujia120103/article/details/75126124 给出了这2个注解的解释,但没有说明具体如何能捕获异常并处理的,通过调试观察调用堆栈,发现是通过springMVC 的handlerExceptionResolvers.resolveException后调用的。这个处理过程应该是如下:
- spring在启动过程通过注解@ExceptionHandler将 方法封装成bean并注入到handlerExceptionResolvers的集合中
- 当controller执行完成后,执行processDispatchResult对结果进行处理
- 调用processHandlerException 方法,遍历handlerExceptionResolvers,执行handlerExceptionResolver.resolveException方法。
springMVC过程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
......
// web的前置处理器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 执行controller的对应方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
// web的后置处理器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//处理controller的执行结果
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
processDispatchResult 中执行processHandlerException方法
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ModelAndView exMv = null;
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
HandlerExceptionResolver handlerExceptionResolver = (HandlerExceptionResolver)var6.next();
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
} else {
if (!exMv.hasView()) {
exMv.setViewName(this.getDefaultViewName(request));
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());
return exMv;
}
} else {
throw ex;
}
}