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:

  1. NoHandlerFoundException: The client request does not find the corresponding controller, and will be thrown404The exception.
  2. 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 exception
  3. HttpMediaTypeNotSupportedExceptionThe request header is then compared to what the controller supports, for examplecontent-typeRequest header if the controller’s parameter signature contains annotations@RequestBody“, but requestedcontent-typeThe 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)
  4. 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:

  1. Create a new class for unified exception handling
  2. Class label@RestControllerAdviceThis one note, or both@ControllerAdviceand@ResponseBodyThese two notes.
  3. Label the method@ExceptionHandlerAnnotations, 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 asExceptionandServiceException.

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 (aMapThe structure,keyIs an exception type,valueIs 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,depthThe 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!!