In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the relevant knowledge of "how to understand the SpringMVC exception handling system". Many people will encounter such a dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
1. Overview of exception parser
In the exception system of SpringMVC, the top big Boss is HandlerExceptionResolver, which is an interface in which there is only one method:
Public interface HandlerExceptionResolver {@ Nullable ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex);}
The resolveException method is used to parse the exception generated during request processing and eventually return a ModelAndView.
Let's take a look at the implementation class of HandlerExceptionResolver:
Three classes directly implement the HandlerExceptionResolver interface:
HandlerExceptionResolverComposite: at first glance, this is another combination. We have seen xxxComposite many times in recent source code analysis, so I won't repeat it here.
DefaultErrorAttributes: this is used to hold exception properties.
AbstractHandlerExceptionResolver: there are many subclasses of this one:
SimpleMappingExceptionResolver: resolve the exception through the correspondence between the pre-configured exception class and the View.
AbstractHandlerMethodExceptionResolver: handles exception types that are customized using the @ ExceptionHandler annotation.
DefaultHandlerExceptionResolver: handle exceptions according to different types.
ResponseStatusExceptionResolver: handles exceptions with @ ResponseStatus annotations.
In SpringMVC, these are roughly the exception parsers, and let's learn about them one by one.
2.AbstractHandlerExceptionResolver
AbstractHandlerExceptionResolver is the parent of the actual working exception parser, so let's start with his resolveException method.
@ Override @ Nullable public ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {if (shouldApplyTo (request, handler)) {prepareResponse (ex, response); ModelAndView result = doResolveException (request, response, handler, ex); if (result! = null) {logException (ex, request);} return result;} else {return null;}}
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
First, call the shouldApplyTo method to determine whether the current parser can handle the exception thrown by the incoming processor. If not, it will return null directly, and this exception will be handed over to the next HandlerExceptionResolver to handle.
Call the prepareResponse method to process the response.
Call the doResolveException method to actually handle the exception, which is a template method, which is implemented in the subclass.
Call the logException method to log exception log information.
There is nothing to say about recording exception logs, but doResolveException is an empty template method, so there are mainly two methods for us: shouldApplyTo and prepareResponse, let's look at them separately.
ShouldApplyToprotected boolean shouldApplyTo (HttpServletRequest request, @ Nullable Object handler) {if (handler! = null) {if (this.mappedHandlers! = null & & this.mappedHandlers.contains (handler)) {return true;} if (this.mappedHandlerClasses! = null) {for (Class handlerClass: this.mappedHandlerClasses) {if (handlerClass.isInstance (handler)) {return true;}} return! hasHandlerMappings ();}
Two objects are involved here: mappedHandlers and mappedHandlerClasses:
MappedHandlers: stores processor objects (methods in Controller or Controller)
MappedHandlerClasses: the Class of the processor is stored.
When we configure the exception parser, we can configure these two objects to realize that the exception handler only serves a certain processor, but generally speaking, there is no such requirement, so we can only understand it.
If the developer initially configured mappedHandlers or mappedHandlerClasses, use these two to compare with the processor, otherwise return true directly, indicating that the exception handling is supported.
PrepareResponse
The prepareResponse method is relatively simple, mainly dealing with the cached fields of the response header.
Protected void prepareResponse (Exception ex, HttpServletResponse response) {if (this.preventResponseCaching) {preventCaching (response);}} protected void preventCaching (HttpServletResponse response) {response.addHeader (HEADER_CACHE_CONTROL, "no-store");}
This is the general content of AbstractHandlerExceptionResolver, you can see that it is still very easy, let's take a look at its implementation class.
2.1 AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver mainly rewrites the shouldApplyTo method and the doResolveException method, one by one.
ShouldApplyTo@Override protected boolean shouldApplyTo (HttpServletRequest request, @ Nullable Object handler) {if (handler = = null) {return super.shouldApplyTo (request, null);} else if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler; handler = handlerMethod.getBean (); return super.shouldApplyTo (request, handler);} else if (hasGlobalExceptionHandlers () & & hasHandlerMappings ()) {return super.shouldApplyTo (request, handler);} else {return false;}}
There is nothing to say about this, and the judgment logic is basically handled by calling the shouldApplyTo method of the parent class.
DoResolveException
Override @ Nullable protected final ModelAndView doResolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {HandlerMethod handlerMethod = (handler instanceof HandlerMethod? (HandlerMethod) handler: null); return doResolveHandlerMethodException (request, response, handlerMethod, ex);} @ Nullable protected abstract ModelAndView doResolveHandlerMethodException (HttpServletRequest request, HttpServletResponse response, @ Nullable HandlerMethod handlerMethod, Exception ex)
DoResolveException is a concrete exception handling method, but there is no substantive operation in it. The concrete things are left to the doResolveHandlerMethodException method, which is an abstract method, and the concrete implementation is in the subclass.
2.1.1 ExceptionHandlerExceptionResolver
The only subclass of AbstractHandlerMethodExceptionResolver is ExceptionHandlerExceptionResolver. Take a look at its doResolveHandlerMethodException method:
@ Override @ Nullable protected ModelAndView doResolveHandlerMethodException (HttpServletRequest request, HttpServletResponse response, @ Nullable HandlerMethod handlerMethod, Exception exception) {ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod (handlerMethod, exception); if (exceptionHandlerMethod = = null) {return null;} if (this.argumentResolvers! = null) {exceptionHandlerMethod.setHandlerMethodArgumentResolvers (this.argumentResolvers);} if (this.returnValueHandlers! = null) {exceptionHandlerMethod.setHandlerMethodReturnValueHandlers (this.returnValueHandlers);} ServletWebRequest webRequest = new ServletWebRequest (request, response); ModelAndViewContainer mavContainer = new ModelAndViewContainer (); ArrayList exceptions = new ArrayList () Try {if (logger.isDebugEnabled ()) {logger.debug ("Using @ ExceptionHandler" + exceptionHandlerMethod);} / / Expose causes as provided arguments as well Throwable exToExpose = exception; while (exToExpose! = null) {exceptions.add (exToExpose); Throwable cause = exToExpose.getCause (); exToExpose = (cause! = exToExpose? Cause: null);} Object [] arguments = new Object [exceptions.size () + 1]; exceptions.toArray (arguments); / efficient arraycopy call in ArrayList arguments [arguments.length-1] = handlerMethod; exceptionHandlerMethod.invokeAndHandle (webRequest, mavContainer, arguments);} catch (Throwable invocationEx) {/ / Any other than the original exception (or a cause) is unintended here, / / probably an accident (e.g. Failed assertion or the like). If (! exceptions.contains (invocationEx) & & logger.isWarnEnabled ()) {logger.warn ("Failure in @ ExceptionHandler" + exceptionHandlerMethod, invocationEx);} / / Continue with default processing of the original exception... Return null;} if (mavContainer.isRequestHandled ()) {return new ModelAndView ();} else {ModelMap model = mavContainer.getModel (); HttpStatus status = mavContainer.getStatus (); ModelAndView mav = new ModelAndView (mavContainer.getViewName (), model, status); mav.setViewName (mavContainer.getViewName ()); if (! mavContainer.isViewReference ()) {mav.setView ((View) mavContainer.getView ()) } if (model instanceof RedirectAttributes) {Map flashAttributes = ((RedirectAttributes) model) .getFlashAttributes (); RequestContextUtils.getOutputFlashMap (request) .putAll (flashAttributes);} return mav;}}
Although this method is long, it is easy to understand:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
First find the method with the @ ExceptionHandler annotation and encapsulate it into a ServletInvocableHandlerMethod object (about the ServletInvocableHandlerMethod object, Songge has already introduced it in the previous article, see: can the method that Spring Boot defines the interface be declared as private?).
If the corresponding method is found, configure parameter parser, view parser and so on for exceptionHandlerMethod. For these parsers, refer to Song GE's previous article: how to customize parameter parser in SpringBoot, how to analyze SpringMVC parameter parser in depth, and how to unify the response format of API interface in SpringBoot?
Next, define an exceptions array, and if the exception that occurs has an exception chain, the entire exception chain is stored in the exceptions array.
Exceptions array plus handlerMethod, together to form method parameters, call exceptionHandlerMethod.invokeAndHandle to complete the execution of custom exception methods, and the execution results are saved in mavContainer.
If the request ends here, a ModelAndView return is constructed directly.
Otherwise, take all the information from the mavContainer and build a new ModelAndView return. At the same time, if there is a redirect parameter, save it (for redirect parameters, see: parameters in SpringMVC can still be passed in this way? Rise in posture!).
This is the general workflow of ExceptionHandlerExceptionResolver, and as you can see, it is still very easy.
2.2 DefaultHandlerExceptionResolver
This is a default exception handler for handling some common exception types. Let's take a look at its doResolveException method:
@ Override @ Nullable protected ModelAndView doResolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {try {if (ex instanceof HttpRequestMethodNotSupportedException) {return handleHttpRequestMethodNotSupported ((HttpRequestMethodNotSupportedException) ex, request, response, handler);} else if (ex instanceof HttpMediaTypeNotSupportedException) {return handleHttpMediaTypeNotSupported ((HttpMediaTypeNotSupportedException) ex, request, response, handler);} else if (ex instanceof HttpMediaTypeNotAcceptableException) {return handleHttpMediaTypeNotAcceptable ((HttpMediaTypeNotAcceptableException) ex, request, response, handler) } else if (ex instanceof MissingPathVariableException) {return handleMissingPathVariable ((MissingPathVariableException) ex, request, response, handler);} else if (ex instanceof MissingServletRequestParameterException) {return handleMissingServletRequestParameter ((MissingServletRequestParameterException) ex, request, response, handler);} else if (ex instanceof ServletRequestBindingException) {return handleServletRequestBindingException ((ServletRequestBindingException) ex, request, response, handler);} else if (ex instanceof ConversionNotSupportedException) {return handleConversionNotSupported (ConversionNotSupportedException) ex, request, response, handler) } else if (ex instanceof TypeMismatchException) {return handleTypeMismatch ((TypeMismatchException) ex, request, response, handler);} else if (ex instanceof HttpMessageNotReadableException) {return handleHttpMessageNotReadable ((HttpMessageNotReadableException) ex, request, response, handler);} else if (ex instanceof HttpMessageNotWritableException) {return handleHttpMessageNotWritable ((HttpMessageNotWritableException) ex, request, response, handler);} else if (ex instanceof MethodArgumentNotValidException) {return handleMethodArgumentNotValidException (MethodArgumentNotValidException) ex, request, response, handler) } else if (ex instanceof MissingServletRequestPartException) {return handleMissingServletRequestPartException ((MissingServletRequestPartException) ex, request, response, handler);} else if (ex instanceof BindException) {return handleBindException ((BindException) ex, request, response, handler);} else if (ex instanceof NoHandlerFoundException) {return handleNoHandlerFoundException ((NoHandlerFoundException) ex, request, response, handler);} else if (ex instanceof AsyncRequestTimeoutException) {return handleAsyncRequestTimeoutException (AsyncRequestTimeoutException) ex, request, response, handler) }} catch (Exception handlerEx) {} return null;}
As you can see, this is actually based on different types of exceptions, and then different classes are called to handle the exception. The relevant handling here is relatively easy. Take HttpRequestMethodNotSupportedException as an example, exception handling is to configure the response object, as follows:
Protected ModelAndView handleHttpRequestMethodNotSupported (HttpRequestMethodNotSupportedException ex, HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler) throws IOException {String [] supportedMethods = ex.getSupportedMethods (); if (supportedMethods! = null) {response.setHeader ("Allow", StringUtils.arrayToDelimitedString (supportedMethods, ",");} response.sendError (HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage ()); return new ModelAndView ();}
Configure the response header, then sendError, and finally return an empty ModelAndView object.
In fact, here brother exception handling methods are more or less the same, Brother Song will not repeat it.
2.3 ResponseStatusExceptionResolver
This is used to handle exceptions of type ResponseStatusException, or a normal exception class marked with the @ ResponseStatus annotation. Let's take a look at its doResolveException method:
@ Override @ Nullable protected ModelAndView doResolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {try {if (ex instanceof ResponseStatusException) {return resolveResponseStatusException ((ResponseStatusException) ex, request, response, handler);} ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation (ex.getClass (), ResponseStatus.class); if (status! = null) {return resolveResponseStatus (status, request, response, handler, ex) } if (ex.getCause () instanceof Exception) {return doResolveException (request, response, handler, (Exception) ex.getCause ());} catch (Exception resolveEx) {} return null;}
As you can see, first determine whether the exception type is ResponseStatusException. If so, call the resolveResponseStatusException method directly to handle the exception information. If not, find the @ ResponseStatus annotation on the exception class, find the relevant exception information, and then call the resolveResponseStatus method to handle it.
As you can see, there are two types of exceptions handled by ResponseStatusExceptionResolver:
Inherits directly from the exception class of ResponseStatusException, which can extract the desired information directly from it.
In this case, the exception information is extracted from the @ ResponseStatus annotation through the normal exception class of the @ ResponseStatus annotation.
This is relatively simple, there is nothing to say.
2.4 SimpleMappingExceptionResolver
SimpleMappingExceptionResolver displays different error pages according to different exceptions. Maybe some of my friends haven't used SimpleMappingExceptionResolver yet, so Brother Song will briefly talk about how to use it here.
The configuration of SimpleMappingExceptionResolver is very simple. You can directly provide an instance of SimpleMappingExceptionResolver, as shown below:
@ Bean SimpleMappingExceptionResolver simpleMappingExceptionResolver () {SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver (); Properties mappings = new Properties (); mappings.put ("java.lang.ArithmeticException", "11"); mappings.put ("java.lang.NullPointerException", "22"); resolver.setExceptionMappings (mappings); Properties statusCodes = new Properties (); statusCodes.put ("11", "500"); statusCodes.put ("22", "500"); resolver.setStatusCodes (statusCodes) Return resolver;}
Configure the corresponding relationship between the exception and view in mappings, write the full path of the exception class, and the following 11 and 22 represent the view name; the mapping relationship between view and response status code is configured in statusCodes. After the configuration is complete, if our project throws an ArithmeticException exception at run time, it will show 11 views, and if our project throws a NullPointerException exception at runtime, it will show 22 views.
This is usage. After understanding the usage, let's look at the source code. It will be easy to understand. Let's look directly at the doResolveException method:
@ Override @ Nullable protected ModelAndView doResolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {String viewName = determineViewName (ex, request); if (viewName! = null) {Integer statusCode = determineStatusCode (request, viewName); if (statusCode! = null) {applyStatusCodeIfPossible (request, response, statusCode);} return getModelAndView (viewName, ex, request);} else {return null;}}
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
First call the determineViewName method to determine the name of the view.
Next, call determineStatusCode to see if the view has a corresponding statusCode.
Call the applyStatusCodeIfPossible method to set the statusCode to response, which is simple and needless to say.
Call the getModelAndView method to construct a ModelAndView object to return. When constructing, set the exception parameters at the same time. The key of the exception information defaults to exception.
In the above process, there are two longer methods. Brother Song, I need to say a few more words to you here.
DetermineViewName
This is to find the view name according to the exception type. Let's take a look at the specific search method:
Nullable protected String determineViewName (Exception ex, HttpServletRequest request) {String viewName = null; if (this.excludedExceptions! = null) {for (Class excludedEx: this.excludedExceptions) {if (excludedEx.equals (ex.getClass () {return null;}} if (this.exceptionMappings! = null) {viewName = findMatchingViewName (this.exceptionMappings, ex);} if (viewName = = null & & this.defaultErrorView! = null) {viewName = this.defaultErrorView;} return viewName;}
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
If the current exception is contained in the excludedExceptions, null is returned directly (meaning that the current exception is ignored and handled directly in the default way).
If the exceptionMappings is not null, call the findMatchingViewName method directly to find the view name corresponding to the exception (the exceptionMappings variable is the mapping relationship we configured earlier). The specific way to find the view is to traverse the mapping table we configured earlier.
If the corresponding viewName is not found and the user has configured defaultErrorView, the defaultErrorView is assigned to viewName and the viewName is returned.
DetermineStatusCode@Nullable protected Integer determineStatusCode (HttpServletRequest request, String viewName) {if (this.statusCodes.containsKey (viewName)) {return this.statusCodes.get (viewName);} return this.defaultStatusCode;}
This is relatively easy, go directly to the statusCodes to see if there is a corresponding status code for the view, if so, return directly, if not, return a default.
3.HandlerExceptionResolverComposite
Finally, there is a HandlerExceptionResolverComposite to introduce to you, this is a combination of exception handlers, which are used to proxy which real working exception handlers.
@ Override @ Nullable public ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, @ Nullable Object handler, Exception ex) {if (this.resolvers! = null) {for (HandlerExceptionResolver handlerExceptionResolver: this.resolvers) {ModelAndView mav = handlerExceptionResolver.resolveException (request, response, handler, ex); if (mav! = null) {return mav;}} return null;}
Its resolveException method is relatively simple, which we have seen many times, so I won't repeat it.
This is the end of "how to understand the SpringMVC exception handling system". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.