Некоторое время назад я написал блог, в котором рассказывалось о том, как я обновил пример кода Spring до версии 3.2, и продемонстрировал несколько возникших маленьких «ошибок». С тех пор как я просматривал новый список возможностей Spring 3.2, и, хотя он не содержит каких-либо революционных изменений, я подозреваю, что ребята из Spring сохраняют его для версии 4, он содержит несколько аккуратных обновлений. Первым, что привлекло мое внимание, была новая аннотация @ControllerAdvice, которая, кажется, аккуратно закрывает пробел в функциональности Spring 3. Позволь мне объяснить…
Если вы посмотрите на мой блог, посвященный обработчикам исключений MVC Spring 3, то увидите, что в примере кода содержится нестабильный контроллер с методом обработчика запросов, который выдает
IOException
. IOException
затем обрабатывается другим методом в том же
контроллер, аннотированный @ExceptionHandler(IOException.class)
. Проблема в том, что ваш метод, аннотированный @ExceptionHandler(IOException.class)
может обрабатывать только IOException
выбрасываемые содержащим его контроллером. Если вы хотите создать глобальный обработчик исключений, который обрабатывает исключения, генерируемые всеми контроллерами, тогда вам нужно вернуться к чему-то вроде SimpleMapingExceptionHandler в Spring 2 и некоторой конфигурации XML . Теперь все по-другому. Чтобы продемонстрировать использование @ControllerAdvice
я создал простое приложение Spring 3.2 MVC, которое вы можете найти на github . Домашняя страница приложения, как правило, позволяет пользователю отображать либо свой адрес, либо данные кредитной карты,
… За исключением того, что когда пользователь пытается сделать это, связанные контроллеры
IOException
и приложение отображает следующую страницу ошибки:
Контроллеры, которые генерируют исключения, довольно просты и перечислены ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Controller public class UserCreditCardController { private static final Logger logger = LoggerFactory.getLogger(UserCreditCardController. class ); /** * Whoops, throw an IOException */ @RequestMapping (value = "userdetails" , method = RequestMethod.GET) public String getCardDetails(Model model) throws IOException { logger.info( "This will throw an IOException" ); boolean throwException = true ; if (throwException) { throw new IOException( "This is my IOException" ); } return "home" ; } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Controller public class UserAddressController { private static final Logger logger = LoggerFactory.getLogger(UserAddressController. class ); /** * Whoops, throw an IOException */ @RequestMapping (value = "useraddress" , method = RequestMethod.GET) public String getUserAddress(Model model) throws IOException { logger.info( "This will throw an IOException" ); boolean throwException = true ; if (throwException) { throw new IOException( "This is my IOException" ); } return "home" ; } } |
Как видите, все, что делает этот код, это сопоставляет userdetails
и useraddress
с getCardDetails(...)
и getUserAddress(...)
соответственно. Когда любой из этих методов генерирует IOException
, исключение перехватывается следующим классом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@ControllerAdvice public class MyControllerAdviceDemo { private static final Logger logger = LoggerFactory.getLogger(MyControllerAdviceDemo. class ); @Autowired private UserDao userDao; /** * 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; } } |
Приведенный выше класс аннотирован новой аннотацией @ControllerAdvice
и содержит единственный открытый метод handleIOException(IOException.class)
. Этот метод перехватывает все исключения IOException, выданные вышеприведенными контроллерами, генерирует модель, содержащую некоторую релевантную информацию о пользователе, а затем отображает страницу ошибок. Приятно то, что независимо от того, сколько контроллеров содержит ваше приложение, когда любой из них выдает IOException
, он будет обрабатываться обработчиком исключений MyControllerAdviceDemo
.
@ModelAttribute
и @InitBinder
одна последняя вещь, которую следует помнить: хотя аннотация ControllerAdvice
полезна для обработки исключений, ее также можно использовать для глобальной обработки аннотаций @ModelAttribute
и @InitBinder
. Комбинация ControllerAdvice
и @ModelAttribute
дает вам возможность настроить объекты модели для всех контроллеров в одном месте, а также комбинация ControllerAdvice
и @InitBinder
позволяет вам присоединить один и тот же пользовательский валидатор ко всем вашим контроллерам, опять же, в одном месте.
Ссылка: обработка исключений с помощью Spring 3.2 @ControllerAdvice Аннотация от нашего партнера по JCG Роджера Хьюза в блоге Captain Debug’s Blog .