Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

Есть несколько способов, чем использовать @ExceptionHandler (см. gouki answer)

  • Вы можете реализовать 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 в этом классе работают глобально.


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

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

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

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

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

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

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

Внедрите простой HandlerExceptionResolver, который "пересылает" исключение из A (уже настроенного) ExceptionHandlerExceptionResolver, но с измененным handler, который указывает на bean, который содержит глобальные обработчики исключений. (Я называю их глобальными, потому что работа для всех контроллеров.).

И это реализация: 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;
    }
}

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

Ответ 2

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

@ControllerAdvice
public class MyGlobalExceptionHandler {

    @ExceptionHandler(value=IOException.class)
    public @ResponseBody String iOExceptionHandler(Exception ex){
        //
        //
    }

    // other exception handler methods
    // ...

}

Ответ 3

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