Continue to original output, click on the top of the blue word to follow me
directory
- preface
- The Spring version of the Boot
- Global unified exception handling past life
- How to classify Spring Boot exceptions?
- How to unify exception handling?
- What is the order of exception matching?
- conclusion
preface
Software development process will inevitably encounter all kinds of bugs, all kinds of exceptions, is always on the way to solve the exception, if your code again try(){… }catch(){… }finally{… } code block, you still have the mood to read? Don’t you feel sick?
Redundant code often loses the motivation to write code, and it is really uncomfortable to write code every day. Try (){try(){… }catch(){… }finally{… }, free your hands.
The Spring version of the Boot
The version of Spring Boot on which this article is based is 2.3.4.release.
Global unified exception handling past life
Back in Spring 3.x, @ControllerAdvice was introduced to work with @ExceptionHandler, @initBinder, @ModelAttribute and other annotations, which are not explained in detail here.
ExceptionHandler only @ExceptionHandler is associated with exceptions, which translates to ExceptionHandler. In fact, the processing of exceptions can be divided into two categories, which are local exception processing and global exception processing.
Local exception handling: The @ExceptionHandler annotation is used with the @Controller annotation. The @ExceptionHandler annotation will only be caught by the @ExceptionHandler if an exception occurs in the specified Controller layer.
Global exception handling: Since local exception handling is not appropriate, it is natural for someone to step up and solve the problem. Hence the @ControllerAdvice annotation, which is combined with @ExceptionHandler to completely solve global exception handling. And then of course there’s @RestControllerAdvice, which is basically @ControllerAdvice and @responseBody.
How to classify Spring Boot exceptions?
There are many exceptions in Java, not to mention the exceptions in Spring Boot. The exceptions are no longer classified according to Java in the traditional sense, but are classified according to controller, including the exceptions before entering controller and the exceptions at the business layer, as shown in the following figure:
Before entering the controller anomalies typically javax.mail. Servlet. ServletException type of exception, so it needs to be in the global exception handling unified handling. A few common exceptions are as follows:
NoHandlerFoundException
: The client request does not find the corresponding controller, and will be thrown404
The exception.HttpRequestMethodNotSupportedException
: If a match is found (the matching result is a list, but HTTP methods are different, such as Get and Post), the system tries to match the HTTP method with the controller of the list. If no controller corresponding to the HTTP method is found, the system throws the exceptionHttpMediaTypeNotSupportedException
The request header is then compared to what the controller supports, for examplecontent-type
Request header if the controller’s parameter signature contains annotations@RequestBody
“, but requestedcontent-type
The value of the request header does not containapplication/json
, the exception will be thrown (of course, this is not the only case where the exception will be thrown)MissingPathVariableException
: Path parameters are not detected. For example, if the URL is /user/{userId}, the parameter signature contains@PathVariable("userId")
When the requested URL is /user, if the url is not explicitly defined as /user, the path parameter is considered to be missing
How to unify exception handling?
There are a number of things that need to be optimized before exception handling can be unified, such as the way results are returned. Of course, I won’t go into details here, so it doesn’t belong to this article.
Unified exception handling is simple, as follows:
- Create a new class for unified exception handling
- Class label
@RestControllerAdvice
This one note, or both@ControllerAdvice
and@ResponseBody
These two notes. - Label the method
@ExceptionHandler
Annotations, and specify the exceptions to catch, which can be multiple at once.
The following is a random configuration of a demo, as follows:
/ * ** Global unified exception handling, simple configuration, according to their own business requirements detailed configuration* /
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler { / * ** Repeat request exception * @param ex * @return * / @ExceptionHandler(RepeatSubmitException.class) public ResultResponse onException(RepeatSubmitException ex){ // Prints logs log.error(ex.getMessage()); // Todo log entry, etc // Unified result is returned return new ResultResponse(ResultCodeEnum.CODE_NOT_REPEAT_SUBMIT); } / * ** Custom business exceptions* / @ExceptionHandler(ServiceException.class) public ResultResponse onException(ServiceException ex){ // Prints logs log.error(ex.getMessage()); // Todo log entry, etc // Unified result is returned return new ResultResponse(ResultCodeEnum.CODE_SERVICE_FAIL); } / * ** Catch some exceptions before entering controller, some 4XX status code is uniformly set to 200 * @param ex * @return * / @ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class, HttpMediaTypeNotAcceptableException.class, MissingPathVariableException.class, MissingServletRequestParameterException.class, ServletRequestBindingException.class, ConversionNotSupportedException.class, TypeMismatchException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MissingServletRequestPartException.class, BindException.class, NoHandlerFoundException.class, AsyncRequestTimeoutException.class}) public ResultResponse onException(Exception ex){ // Prints logs log.error(ex.getMessage()); // Todo log entry, etc // Unified result is returned return new ResultResponse(ResultCodeEnum.CODE_FAIL); } } Copy the code
Note: The above is just an example, there are many exceptions to catch in real development, such as TOKEN expiration, expiration, etc. If you integrate other frameworks, you should also be aware of the exceptions thrown by these frameworks, such as Shiro, Spring Security, etc.
What is the order of exception matching?
If I catch both parent and child classes, which exception handler can I catch? Such asException
andServiceException
.
If there is no ServiceException handler, then its parent will be called. If there is no ServiceException handler, then the parent will be called. If there is no ServiceException handler, then the parent will be called. In a word, exact match, find the closest one.
Why is that? This is not just talk nonsense, source code, source org. Springframework. Web. Method. The annotation. ExceptionHandlerMethodResolver# getMappedMethod, as follows:
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
// Iterate over the exception type defined in the exception handler
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
// Is the parent class to throw the exception, if added to the collection if (mappedException.isAssignableFrom(exceptionType)) { // Add to the collection matches.add(mappedException); } } // If the set is not empty, sort by the rules if(! matches.isEmpty()) { matches.sort(new ExceptionDepthComparator(exceptionType)); // take the first one return this.mappedMethods.get(matches.get(0)); } else { return null; } } Copy the code
The code above is executed to find the exception handler method that best matches the first exception handling, and then directly from the cache (aMap
The structure,key
Is an exception type,value
Is the exception handler method.
Select ExceptionDepthComparator from ExceptionDepthComparator (ExceptionDepthComparator);
// Get depth recursively. The smaller the depth, the more accurate the match
private int getDepth(Class<? > declaredException, Class<? > exceptionToMatch,int depth) {
// If a match is found, return
if (exceptionToMatch.equals(declaredException)) {
// Found it!
return depth; } // The recursive end condition is maximized if (exceptionToMatch == Throwable.class) { return Integer.MAX_VALUE; } // Continue matching the parent class return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1); } Copy the code
That’s all there is, one recursion done, depth computed,depth
The initial value is 0. The smaller the value is, the more accurate the matching degree is.
conclusion
There are thousands of articles on global exceptions, how many can be explained clearly? Only out of the most fine article, do the most wild programmer, if you feel good, pay attention to share a wave, thank you for your support!!