Передача ошибок обратно в представление из служебного уровня

Изменить: Я просмотрел аннотацию Spring 3 @ExceptionHandler, и объединение этого с вариантом 1 ниже выглядит довольно простым решением.

См. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers

Я также нашел, что это хороший текст: http://blog.decaresystems.ie/index.php/2006/04/07/difficult-choices-in-handling-exceptions-in-enterprise-java-applications/


Я уже некоторое время разрабатываю с помощью среды Spring MVC, однако я изо всех сил стараюсь придумать "хороший" способ передать ошибки, поднятые в служебном слое обратно в JSP.

В принципе, я не считаю, что бизнес-логика (за исключением "это обязательное поле" ) должна быть в валидаторах, особенно в любой логике, требующей доступа к БД. Таким образом, то, что я делал, заключается в размещении на уровне обслуживания еще более сложной проверки и бизнес-логики.

Например, скажем, у меня есть страница, которая позволяет пользователю покупать книгу. Они нажимают "Купить" на JSP, и контроллер вызывает службу, чтобы все это произошло... Теперь, что произойдет, если служба увидит, что у них недостаточно средств - как я могу вернуть это сообщение в JSP, Сообщение "Недостаточно средств" может быть отображено пользователю? Я рассмотрел два пути, и я не уверен, что правильно...

Вариант 1: Исключения

Первый способ, по моему мнению, - создать исключение на уровне сервиса, запереть его в контроллере и добавить сообщение в BindingResult.

Услуги:

public void pay(Book book) throws InsufficientFundsException {
    // Some logic goes here, which ends up throwing the above exception
}

Контроллер:

public ModelAndView(@ModelAttribute("book") Book book, BindingResult errors) {
    try {
        pay(book);
    } catch (InsufficientFundsException ex) {
        errors.reject("insufficient.funds");
    }
    return new ModelAndView(blahblahblah);
}

Вариант 2: пересылка BindingResult на сервисный уровень

Второй способ состоял в том, чтобы передать объект BindingResult на уровень сервиса и вызывать дополнительные ошибки.

Услуги:

public void pay(Book book, BindingResult errors) {
    // User has insufficient funds, so...
    errors.reject("insufficient.funds);
}

Я вижу проблемы с обоими этими способами. Вариант 1 чувствует себя неловко, потому что не только я должен поймать исключение, я должен добавить ошибку к результату привязки, поэтому мне кажется, что я делаю то же самое дважды. А вариант 2, похоже, слишком сильно привязывает сервисный уровень к контроллеру.

Наконец, я понимаю, что есть SimpleMappingExceptionResolver, который может использоваться в сочетании с Вариантом 1, но я не уверен, насколько это уместно (возможно, я не видел подходящего примера?). В приведенном выше примере давайте просто скажем для аргумента ради того, что я хотел бы, чтобы пользователь вернулся к исходной форме с красной ошибкой над формой, а не перенаправлен на совершенно другую страницу. SimpleMappingExceptionResolver мне кажется полезным, когда вы хотите перенаправить пользователя на стандартную страницу ошибок, когда возникает определенное исключение (что совсем не то, что я хочу знать, как это сделать).

Ответ 1

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

Я не понимаю, почему представленный вами случай отличается от любого другого случая, когда я использовал обработку исключений для обработки ошибок.