Статьи

Spring 3 MVC Обработчики исключений

Кажется, что большая часть примера кода обработки ошибок Spring 3, с которым я когда-либо сталкивался, дает простейший обзор его использования, однако то, как мы работаем с ошибками, более важно, чем то, как работает нормальный код. Это было подтверждено на днях, когда я наткнулся на простую ‘GOTCHA’ в обработчике ошибок Spring (2), которая вывела весь сайт из строя и почти убила сервер, но об этом позже.

В сегодняшнем блоге рассматривается сценарий создания простого обработчика исключений сервлета Spring 3 с использованием аннотации @ ExceptionHandler . Хотя вы, возможно, видели это до того, как он предоставил мне хорошее место для начала, и для этой демонстрации я создал простое веб-приложение MVC для Spring 3 ( 1), где домашняя страница ( home.jsp ) отправляет запросы классу нестабильных контроллеров. который генерирует исключения ( ExceptionDemoController )

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
   * Whoops, throw an IOException
   */
  @RequestMapping(value = "/ioexception", method = RequestMethod.GET)
  public String throwIoException(Locale locale, Model model) throws IOException {
 
    logger.info("This will throw an IOExceptiom");
 
    boolean throwException = true;
 
    if (throwException) {
      throw new IOException("This is my IOException");
    }
 
    return "home";
  }

Слабый код контроллера выше — это первый шаг в генерации ошибки. Идея состоит в том, что он должен вернуть пользователя на нашу домашнюю страницу, но при обработке запроса пользователя он создает простое исключение IOException . После выдачи исключение перехватывается этим методом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/**
   * Catch IOException and redirect to a 'personal' page
   */
  @ExceptionHandler(IOException.class)
  public ModelAndView handleIOException(IOException ex) {
 
    logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
    return errorModelAndView(ex);
  }
 
  /**
   * Get the users details for the 'personal' page
   */
  private ModelAndView errorModelAndView(Exception ex) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("name", ex.getClass().getSimpleName());
    modelAndView.addObject("user", userDao.readUserName());
 
    return modelAndView;
  }

Чтобы установить это действительно просто, все, что вам нужно сделать, это добавить:

1
@ExceptionHandler(IOException.class)

… к сигнатуре метода, и вуаля, вы закончили … и с этим немного покончено.

Здесь стоит отметить несколько моментов: во-первых, используя

1
@ExceptionHandler(IOException.class)

… Будет придерживаться обычного контракта на обработку исключений. Это означает, что метод не только перехватит все исключения IOException , но и все исключения, которые являются подклассами IOException ; следовательно, если мой метод throwAnException (..) выдал исключение FileNotFoundException, оно все равно будет перехвачено моим методом handleIOException (…).

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

  • Исключение или один из его подклассов
  • ServletRequest или HttpServletRequest
  • ServletResponse или HttpServletResponse
  • HttpSession
  • WebRequest или NativeWebRequest
  • место действия
  • InputStream или один из его подклассов для доступа к содержимому запроса
  • OutputStream или один из его подклассов для доступа к содержимому ответа
  • Читатель или один из его подклассов
  • Писатель или один из его подклассов

Подпись метода также должна иметь один из следующих типов возврата:

  • ModelAndView
  • модель
  • карта
  • Посмотреть
  • Строка — интерпретируется как имя представления
  • void, но только если метод записывает непосредственно в объект ответа

Все это должно быть достаточно для любого сценария при любых обстоятельствах.

Использование @ ExceptionHandler дает вам возможность выполнять детальную обработку исключений, предназначенную для различных сценариев ошибок. В случае примера кода я создаю новый объект ModelAndView и заполняю его именем пользователя, чтобы лично сообщить ему, что система потеряла их документы. Некоторые могут сказать, что это ограничение, поскольку @ ExceptionHandler настолько детализирован, что вы можете перехватывать только те исключения, которые выдает контроллер, содержащий ваш аннотированный метод @ ExceptionHandler . Я бы не согласился, если вы хотите перехватывать исключения, генерируемые несколькими контроллерами в одном месте, тогда этот метод не для вас, и вам следует рассмотреть возможность использования SimpleMappingExceptionResolver .

При реализации обработки ошибок есть много моментов, таких как: что произойдет, если в вашем обработчике ошибок есть ошибка? Вы должны использовать грубые или мелкозернистые обработчики исключений? Как насчет настройки кода статуса HTTP? Итак, мои следующие несколько блогов будут посвящены дальнейшей обработке ошибок, демонстрируя, как назначить несколько классов исключений одному @ExceptionHandler и как объединить нотацию обработчика исключений с @ResponseStatus для точной настройки кода состояния HTTP вашего сервера, и может быть больше…

Справка: обработчики исключений Spring 3 MVC от нашего партнера JCG  


  1. Полный образец веб-приложения доступен по адресу:
    мерзавец: //github.com/roghughe/captaindebug.git
  2. См. Spring документацию для справочного материала.