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
@ControllerAdvice
Annotations (to make sure they are scanned as beans) and there are contains under the class@ExceptionHandler
Annotation method - The controller class of the currently executing method has a
@ExceptionHandler
Annotation method