복붙노트

[SPRING] Spring MVC의 예외 처리기

SPRING

Spring MVC의 예외 처리기

내 프로젝트의 모든 컨트롤러를 가로채는 예외 처리기를 만들고 싶습니다. 그렇게 할 수 있습니까? 모든 컨트롤러에 처리기 메서드를 넣어야하는 것 같습니다. 당신의 도움을 주셔서 감사합니다. Json 응답을 보내는 스프링 컨트롤러가 있습니다. 따라서 예외가 발생하면 한 곳에서 제어 할 수있는 오류 응답을 보내려고합니다.

해결법

  1. ==============================

    1.(스프링 3.1에서 구현하는 방법을 찾았는데,이 내용은이 답변의 두 번째 부분에서 설명합니다)

    (스프링 3.1에서 구현하는 방법을 찾았는데,이 내용은이 답변의 두 번째 부분에서 설명합니다)

    Chapter 16.11 Spring 참조의 예외 처리를 보라.

    @ExceptionHandler를 사용하는 것보다 더 많은 방법이 있습니다 (gouki의 대답 참조).

    Spring 3.2 이상에서는 @ControllerAdvice 클래스에 주석을 달 수 있습니다.이 클래스의 모든 @ExceptionHandler 메소드는 전역 적으로 작동합니다.

    Spring 3.1에는 @ControllerAdvice가 없다. 그러나 약간의 해킹으로 비슷한 기능을 가질 수 있습니다.

    핵심은 @ExceptionHandler가 작동하는 방식에 대한 이해입니다. Spring 3.1에는 ExceptionHandlerExceptionResolver 클래스가있다. 이 클래스는 슈퍼 클래스의 도움으로 HandlerExceptionResolver 인터페이스를 구현하고 @ExceptionHandler 메소드를 호출합니다.

    HandlerExceptionResolver 인터페이스에는 Method가 하나만 있습니다.

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

    요청이 Spring 3.x Controller Method에 의해 처리되면, org.springframework.web.method.HandlerMethod로 대표되는이 메소드는 핸들러 매개 변수이다.

    ExceptionHandlerExceptionResolver는 핸들러 (HandlerMethod)를 사용하여 Controller 클래스를 가져 와서 @ExceptionHandler로 주석 된 메소드를 스캔합니다. 이 메소드의 1 개가 예외 (ex)에 일치하는 경우,이 메소드는 예외를 처리하기 위해서 불려갑니다. (이 예외 해결 프로그램이 아무런 책임이 없음을 알리기 위해 null이 반환됩니다).

    첫 번째 아이디어는 ExceptionHandlerExceptionResolver처럼 작동하는 자체 HandlerExceptionResolver를 구현하는 것이지만 컨트롤러 클래스에서 @ExceptionHandler를 검색하는 대신 하나의 특수 bean에서이를 검색해야합니다. 단점은, 모든 좋은 메시지 변환기, 인수 확인 자 및 반환 값 처리기를 수동으로 구성해야한다는 것입니다 (실제 구성의 구성은 봄에 자동으로 수행됩니다). 그래서 다른 아이디어를 생각해 냈습니다.

    전역 Exception 핸들러를 포함하는 bean을 가리키는 수정 된 핸들러를 사용하여 ExceptionHandlerExceptionResolver에 (이미 구성된) Exception을 "전달"하는 간단한 HandlerExceptionResolver를 구현하십시오. (나는 모든 컨트롤러에 대한 작업 때문에 전역이라고 부른다.).

    그리고 이것은 구현입니다 : 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);
            }            
        }
    }
    

    글로벌 Handler는이 인터페이스를 구현해야한다 (찾기 위해 그리고 핸들러에 사용 된 fakeHanderMethod를 구현하기 위해)

    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 : Spring은 Exception resolver를 위해 HandlerExceptionResolver를 구현하는 모든 bean을 자동으로 등록하기 때문에 GlobalMethodHandlerExeptionResolver를 등록 할 필요가 없습니다. 그래서 간단한 로 충분하다.

  2. ==============================

    2.Spring 3.2부터 @ControllerAdvice 주석을 사용할 수있다. @ControllerAdvice 클래스 내에서 @ExceptionHandler 메서드를 선언 할 수 있습니다. 이 경우 모든 컨트롤러에서 @RequestMapping 메서드의 예외를 처리합니다.

    Spring 3.2부터 @ControllerAdvice 주석을 사용할 수있다. @ControllerAdvice 클래스 내에서 @ExceptionHandler 메서드를 선언 할 수 있습니다. 이 경우 모든 컨트롤러에서 @RequestMapping 메서드의 예외를 처리합니다.

    @ControllerAdvice
    public class MyGlobalExceptionHandler {
    
        @ExceptionHandler(value=IOException.class)
        public @ResponseBody String iOExceptionHandler(Exception ex){
            //
            //
        }
    
        // other exception handler methods
        // ...
    
    }
    
  3. ==============================

    3.예외 처리기를 정의하는 추상 클래스입니다. 그리고 컨트롤러가 그것을 상속하도록하십시오.

    예외 처리기를 정의하는 추상 클래스입니다. 그리고 컨트롤러가 그것을 상속하도록하십시오.

  4. from https://stackoverflow.com/questions/6742324/exception-handler-in-spring-mvc by cc-by-sa and MIT license