We cannot write interfaces that are error-free, and we need mechanisms to handle exceptions in case they do occur.

Exception handling lookup

DispatcherServlet->doDispatch(): mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); catch (Exception ex) { dispatchException = ex; } {dispatchException = new NestedServletException("Handler Dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); DispatcherServlet->processDispatchResult(): dispatchServlet ->processDispatchResult(): dispatchServlet ->processDispatchResult(): = null) { if (exception instanceof ModelAndViewDefiningException) { mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler ! = null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv ! = null); } } DispatcherServlet->processHandlerException(): / / the handlerExceptionResolvers here is assigned a value when the Servlet is initialized if (this. HandlerExceptionResolvers! = null) { for (HandlerExceptionResolver resolver : Enclosing handlerExceptionResolvers) {/ / DefaultErrorAttributes assignment, the property of the request Returns null exMv = resolver. ResolveException (request, response, handler, the ex); if (exMv ! = null) { break; }}}Copy the code

Determine whether there is abnormal before return to view, if there is one exception, deal with it by the servlet handlerExceptionResolvers under variable, And will eventually perform HandlerExceptionResolverComposite resolveException under class () method.

HandlerExceptionResolverComposite->resolveException(): for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) { ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex); if (mav ! = null) { return mav; } } AbstractHandlerExceptionResolver->resolveException(): ModelAndView result = doResolveException(request, response, handler, ex); return result; AbstractHandlerMethodExceptionResolver->doResolveException(): return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); ExceptionHandlerExceptionResolver->doResolveHandlerMethodException(): ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); ExceptionHandlerExceptionResolver->getExceptionHandlerMethod(): handlerType = handlerMethod.getBeanType(); ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); If (resolver = = null) {/ / find the class have a @ ExceptionHandler annotation methods resolver = new ExceptionHandlerMethodResolver (handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } // Whether this exception conforms to the exception defined by @ExceptionHandler of this class Method Method = resolver.resolvemethod (exception); if (method ! = null) {/ / have it directly back to return new ServletInvocableHandlerMethod (handlerMethod) getBean (), method); }... for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); if (advice.isApplicableToBeanType(handlerType)) { ExceptionHandlerMethodResolver resolver = entry.getValue(); Method method = resolver.resolveMethod(exception); if (method ! = null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } return null;Copy the code

The resolver.resolvemethod (exception) method checks whether the exception is consistent with the exception defined by @ExceptionHandler. If so, the method is returned. If there are more than one exception, the most appropriate one is selected. If none of these matches, springBoot defaults to the jump /error interface

Exception Handling Definition

@ControllerAdvice public class ExceptionAdvice { @ExceptionHandler(Exception.class) @ResponseBody public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) { Map<String, Object> map = new HashMap<String, Object>(); map.put("error", error.getMessage()); map.put("result", "error"); return map; }}Copy the code

Will start stage, from the container for containing @ ControllerAdvice annotations of bean, then traverse beans, figure out how to contain @ ExceptionHandler assigned to exceptionHandlerAdviceCache variables

Code in ExceptionHandlerExceptionResolver – > afterPropertiesSet ()

Exception handling

ExceptionHandlerExceptionResolver->doResolveHandlerMethodException(): // exceptionHandlerMethod includes exceptionHandlerMethod, exceptionHandlerMethod, exceptionHandlerMethod, exceptionHandlerMethod Process the return value exceptionHandlerMethod. InvokeAndHandle (webRequest mavContainer, exception, handlerMethod); If (mavContainer isRequestHandled ()) {/ / if the method contains @ ResponseBody annotations, return empty modelAndView return new modelAndView (); } else {// Return modelAndView with arguments...... }Copy the code
DispatcherServlet->processDispatchResult(): if (exMv ! Return null if (exmv.isempty ()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // Otherwise return the view if (! exMv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName ! = null) { exMv.setViewName(defaultViewName); } } return exMv; } DispatcherServlet->processDispatchResult(): if (mv ! = null && ! Mv.wascleared ()) {// Render (mv, request, response); }Copy the code

As you can see, if an exception occurs and is caught by an exception handler, the exception handler is executed and its view is returned

conclusion

The exception handling method must meet either of the following two conditions:

  • Class there@ControllerAdviceAnnotations (to make sure they are scanned as beans) and there are contains under the class@ExceptionHandlerAnnotation method
  • The controller class of the currently executing method has a@ExceptionHandlerAnnotation method