Статьи

Обработка исключений с помощью Spring 3.2 @ControllerAdvice Аннотация

Некоторое время назад я написал блог, в котором рассказывалось о том, как я обновил пример кода 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 .