Девятая пуля Spring Boot, полноэкранный пробный режим, не паникуйте?

Spring Boot

Продолжить первоначальный вывод, нажмите синее слово выше, чтобы следовать за мной

содержание

  • предисловие
  • Весенняя загрузочная версия
  • Прошлое и настоящее глобальной унифицированной обработки исключений
  • Как классифицируются исключения Spring Boot?
  • Как унифицировать обработку исключений?
  • В каком порядке сопоставляются исключения?
  • Суммировать

предисловие

В процессе разработки программного обеспечения неизбежно столкновение с различными ошибками и различными исключениями.Он всегда был на пути к устранению исключения и никогда не остановится.Если ваш код появится сноваtry(){...}catch(){...}finally{...}Кодовый блок, вы все еще в настроении его прочитать? Вы не чувствуете себя больным?

Избыточный код часто лишает мотивации писать код, а писать код каждый день так же неудобно, как передвигать кирпичи. Сегодняшняя статья научит вас, как убрать полноэкранный режимtry(){...}catch(){...}finally{...}, освободите руки.

Весенняя загрузочная версия

Версия Spring Boot, на которой основана эта статья:2.3.4.RELEASE.

Прошлое и настоящее глобальной унифицированной обработки исключений

ужеSpring 3.xуже предложено@ControllerAdvice,С участием@ExceptionHandler,@InitBinder,@ModelAttributeКогда аннотации используются вместе, они не будут подробно объясняться здесь.

С первого взгляда на эти несколько аннотаций, только@ExceptionHandlerЭто связано с исключением, перевод异常处理器.На самом деле обработку исключений можно разделить на две категории, а именно:局部异常处理и全局异常处理.

局部异常处理:@ExceptionHandlerи@ControllerПри использовании вместе с аннотациями в случае исключения будет вызываться только указанный уровень контроллера.@ExceptionHandlerЗахвачено, контроллеров в реальном производстве наверное сотни.Очевидно, что этот способ не подходит.

全局异常处理: поскольку локальная обработка исключений неуместна, кто-то, естественно, встанет, чтобы решить проблему, поэтому@ControllerAdviceЭта аннотация родилась из ниоткуда,@ControllerAdviceсовпадение@ExceptionHandlerПолностью решить глобальную унифицированную обработку исключений. Конечно, было позже@RestControllerAdviceЭта заметка на самом деле@ControllerAdviceи@ResponseBodyкристаллизация.

Как классифицируются исключения Spring Boot?

В Java есть много исключений, не говоря уже об исключениях в Spring Boot, которые классифицируются уже не по Java-исключениям в традиционном смысле, а поcontrollerклассифицировать на进入controller前的异常и业务层的异常,Как показано ниже:

Исключение перед входом в контроллер обычноjavax.servlet.ServletExceptionтип исключения, поэтому его необходимо обрабатывать единообразно при обработке глобальных исключений. Вот несколько общих исключений:

  1. NoHandlerFoundException: клиентский запрос не находит соответствующий контроллер, выбрасывает404аномальный.
  2. HttpRequestMethodNotSupportedException: Если есть совпадение (результатом сопоставления является список, разница в том, что http-методы разные, такие как: Get, Post и т. д.), то попробуйте сопоставить запрошенный http-метод с контроллером списка. Если нет контроллера, соответствующего методу http, выдать исключение
  3. HttpMediaTypeNotSupportedException: Затем сравните заголовки запроса с поддерживаемыми контроллером, такими какcontent-typeзаголовок запроса, если подпись параметра контроллера содержит аннотации@RequestBody, но запрошенныйcontent-typeЗначение заголовка запроса не содержитapplication/json, то будет выброшено исключение (разумеется, это исключение будет выброшено не только в этом случае)
  4. MissingPathVariableException: Параметр пути не обнаружен. Например, URL-адрес: /user/{userId}, а подпись параметра содержит@PathVariable("userId"), когда запрошенный URL-адрес /user, если URL-адрес четко не определен как /user, он будет оценен как: параметр пути отсутствует

Как унифицировать обработку исключений?

Прежде чем унифицировать обработку исключений, на самом деле нужно оптимизировать многие вещи, например форму возврата унифицированных результатов. Конечно, я не буду вдаваться в подробности здесь, и это не относится к теме данной статьи.

Унифицированная обработка исключений очень проста. Вот пример проекта, разделенного на переднюю и заднюю часть. Шаги следующие::

  1. Создайте новый класс для унифицированной обработки исключений.
  2. ярлык на классе@RestControllerAdviceэта аннотация или обе@ControllerAdviceи@ResponseBodyэти две аннотации.
  3. метка на методе@ExceptionHandlerАннотируйте и укажите исключения, которые нужно перехватывать, которые можно перехватывать одновременно.

Ниже приведена случайная конфигурация авторской демонстрации:

/**
 * 全局统一的异常处理,简单的配置下,根据自己的业务要求详细配置
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    /**
     * 重复请求的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(RepeatSubmitException.class)
    public ResultResponse onException(RepeatSubmitException ex){
        //打印日志
        log.error(ex.getMessage());
        //todo 日志入库等等操作

        //统一结果返回
        return new ResultResponse(ResultCodeEnum.CODE_NOT_REPEAT_SUBMIT);
    }


    /**
     * 自定义的业务上的异常
     */
    @ExceptionHandler(ServiceException.class)
    public ResultResponse onException(ServiceException ex){
        //打印日志
        log.error(ex.getMessage());
        //todo 日志入库等等操作

        //统一结果返回
        return new ResultResponse(ResultCodeEnum.CODE_SERVICE_FAIL);
    }


    /**
     * 捕获一些进入controller之前的异常,有些4xx的状态码统一设置为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){
        //打印日志
        log.error(ex.getMessage());
        //todo 日志入库等等操作

        //统一结果返回
        return new ResultResponse(ResultCodeEnum.CODE_FAIL);
    }
}

Уведомление:Вышеприведенное является лишь примером, есть много исключений, которые необходимо поймать в реальной разработке, напримерTOKEN失效,过期Дождитесь исключений.Если вы интегрируете другие фреймворки, вам также следует обратить внимание на исключения, генерируемые этими фреймворками, такие какShiro,Spring Securityи так далее кадр.

В каком порядке сопоставляются исключения?

Некоторым друзьям может быть интересно, если я захватываю и родительский класс, и подкласс, может ли меня поймать этот обработчик исключений? НапримерExceptionиServiceException.

В этот момент у вас могут возникнуть подозрения.Вот ответ сначала, конечноServiceExceptionОбработчик исключения пойман, точное совпадение, если нетServiceExceptionобработчик исключений получит свою очередь父亲,父亲это не придет祖父. Словом, точное совпадение, найти ближайшее родство.

Зачем? Это не бред, исходник тому доказательство, исходникorg.springframework.web.method.annotation.ExceptionHandlerMethodResolver#getMappedMethod,следующее:

@Nullable
 private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
  List<Class<? extends Throwable>> matches = new ArrayList<>();
    //遍历异常处理器中定义的异常类型
  for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
      //是否是抛出异常的父类,如果是添加到集合中
   if (mappedException.isAssignableFrom(exceptionType)) {    
        //添加到集合中
    matches.add(mappedException);  
   }
  }
    //如果集合不为空,则按照规则进行排序
  if (!matches.isEmpty()) {
   matches.sort(new ExceptionDepthComparator(exceptionType));
      //取第一个
   return this.mappedMethods.get(matches.get(0));
  }
  else {
   return null;
  }
 }

При первой обработке исключений приведенный выше код будет выполняться для поиска наиболее подходящего метода обработчика исключений, а последующие будут выполняться непосредственно из кеша (aMapструктура,keyтип исключения,valueявляется методом обработчика исключений).

Не волнуйтесь, наиболее существенная часть приведенного выше кода верна.matchesКод для сортировки готов, давайте посмотримExceptionDepthComparatorКлючевой код этого компаратора выглядит следующим образом:

//递归调用,获取深度,depth值越小越精准匹配
private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch, int depth) {
    //如果匹配了,返回
  if (exceptionToMatch.equals(declaredException)) {
   // Found it!
   return depth;
  }
  // 递归结束的条件,最大限度了
  if (exceptionToMatch == Throwable.class) {
   return Integer.MAX_VALUE;
  }
    //继续匹配父类
  return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);
 }

Суть тут вся, рекурсия сделана, глубина посчитана,depthНачальное значение равно 0. Чем меньше значение, тем выше и точнее совпадение.

Суммировать

Есть тысячи статей о глобальных аномалиях, сколько из них можно объяснить доходчиво?Производите только самые изысканные статьи и будьте самыми смелыми программистами, Если вы думаете, что это хорошо, пожалуйста, подпишитесь и поделитесь, спасибо за вашу поддержку! ! !