Обработчик исключений в Spring MVC - PullRequest
20 голосов
/ 19 июля 2011

Я хочу создать обработчик исключений, который будет перехватывать все контроллеры в моем проекте.Это возможно сделать?Похоже, я должен поместить метод обработчика в каждый контроллер.Спасибо за вашу помощь.У меня есть пружинный контроллер, который отправляет Json ответ.Поэтому в случае возникновения исключения я хочу отправить ответ об ошибке, которым можно управлять из одного места.

Ответы [ 3 ]

23 голосов
/ 19 июля 2011

(я нашел способ реализовать это в Spring 3.1, это описано во второй части этого ответа)

См. Главу 16.11. Обработка исключений Spring Reference

Есть несколько способов, кроме использования @ExceptionHandler (см. ответ Гуки )

  • Вы можете реализовать HandlerExceptionResolver ( использовать сервлет, а не пакет портлета ) - это своего рода глобальный @ ExceptionHandler
  • Если у вас нет определенной логики для исключения, а есть только конкретное представление, вы можете использовать SimpleMappingExceptionResolver , который является по крайней мере реализацией HandlerExceptionResolver, где вы можете указать шаблон имени исключения и представление (jsp), которое отображается при создании исключения. Например:

    <bean
       class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
       p:defaultErrorView="uncaughtException">
       <property name="exceptionMappings">
           <props>
               <prop key=".DataAccessException">dataAccessFailure</prop>
               <prop key=".TypeMismatchException">resourceNotFound</prop>
               <prop key=".AccessDeniedException">accessDenied</prop>
            </props>
        </property>
     </bean>
    

В Spring 3.2 + можно аннотировать класс с помощью @ControllerAdvice, все методы @ExceptionHandler в этом классе работают глобально.


В Пружина 3.1 нет @ControllerAdvice. Но с небольшим взломом можно получить аналогичную функцию.

Ключом является понимание того, как @ExceptionHandler работает. Весной 3.1 есть класс ExceptionHandlerExceptionResolver. Этот класс реализует (с помощью своих суперклассов) интерфейс HandlerExceptionResolver и отвечает за вызов методов @ExceptionHandler.

Интерфейс HandlerExceptionResolver имеет только один метод:

ModelAndView resolveException(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              Exception ex);`.

Когда запрос обрабатывался методом контроллера Spring 3.x, тогда этот метод (представленный org.springframework.web.method.HandlerMethod) является параметром handler.

ExceptionHandlerExceptionResolver использует handler (HandlerMethod) для получения класса Controller и сканирования его на наличие методов, помеченных @ExceptionHandler. Если один из этих методов соответствует исключению (ex), то эти методы вызываются для обработки исключения. (иначе null возвращают, чтобы сигнализировать, что этот обработчик исключений не чувствует ответственности).

Первой идеей было бы реализовать собственный HandlerExceptionResolver, который ведет себя как ExceptionHandlerExceptionResolver, но вместо поиска @ExceptionHandler в классе контроллера он должен искать их в одном специальном компоненте. Недостатком было бы то, что нужно (скопировать (или подкласс ExceptionHandlerExceptionResolver) и должен) настроить все хорошие конвертеры сообщений, средства разрешения аргументов и обработчики возвращаемых значений вручную (конфигурация реального и только ExceptionHandlerExceptionResolver выполняется пружина автоматически). Поэтому я пришел с другой идеей:

Реализация простого HandlerExceptionResolver, который «перенаправляет» исключение в THE (уже настроено) ExceptionHandlerExceptionResolver, НО с измененным handler, указывающим на компонент, содержащий глобальные обработчики исключений. (Я называю их глобальными, потому что работа для всех контроллеров.).

И это реализация: GlobalMethodHandlerExeptionResolver

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;


public class GlobalMethodHandlerExeptionResolver
             implements HandlerExceptionResolver, Ordered {

    @Override
    public int getOrder() {
        return -1; //
    }

    private ExceptionHandlerExceptionResolver realExceptionResolver;

    private List<GlobalMethodExceptionResolverContainer> containers;

    @Autowired
    public GlobalMethodHandlerExeptionResolver(
            ExceptionHandlerExceptionResolver realExceptionResolver,
            List<GlobalMethodExceptionResolverContainer> containers) {
        this.realExceptionResolver = realExceptionResolver;
        this.containers = containers;
    }

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Object handler,
                                         Exception ex) {              
        for (GlobalMethodExceptionResolverContainer container : this.containers) {    
            ModelAndView result = this.realExceptionResolver.resolveException(
                    request,
                    response,
                    handlerMethodPointingGlobalExceptionContainerBean(container),
                    ex);
            if (result != null)
                return result;
        }
        // we feel not responsible
        return null;
    }


    protected HandlerMethod handlerMethodPointingGlobalExceptionContainerBean(
                               GlobalMethodExceptionResolverContainer container) {
        try {
            return new HandlerMethod(container,
                                     GlobalMethodExceptionResolverContainer.class.
                                          getMethod("fakeHanderMethod"));            
        } catch (NoSuchMethodException | SecurityException e) {
            throw new RuntimeException(e);
        }            
    }
}

Глобальный обработчик должен реализовать этот интерфейс (чтобы найти и реализовать fakeHanderMethod, используемый для handler

public interface GlobalMethodExceptionResolverContainer {
    void fakeHanderMethod();
}

И пример для глобального обработчика:

@Component
public class JsonGlobalExceptionResolver
             implements GlobalMethodExceptionResolverContainer {

    @Override
    public void fakeHanderMethod() {
    }


    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ValidationErrorDto handleMethodArgumentNotValidException(
                MethodArgumentNotValidException validationException,
                Locale locale) {

         ...
         /* map validationException.getBindingResult().getFieldErrors()
          * to ValidationErrorDto (custom class) */
         return validationErrorDto;
    }
}

Кстати: вам не нужно регистрировать GlobalMethodHandlerExeptionResolver, потому что spring автоматически регистрирует все bean-компоненты, которые реализуют HandlerExceptionResolver для распознавателей исключений. Так что простого <bean class="GlobalMethodHandlerExeptionResolver"/> достаточно.

15 голосов
/ 18 апреля 2013

С весны 3.2 вы можете использовать аннотацию @ ControllerAdvice .Вы можете объявить метод @ ExceptionHandler в классе @ControllerAdvice, в этом случае он обрабатывает исключения из методов @RequestMapping из всех контроллеров.

9 голосов
/ 19 июля 2011

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

...