ExceptionHandler_언제_어떻게

@ExceptionHandler: 언제 어떻게 동작하는가.

  • @RequestParam이나 @Pathvariable, @RequestBody 등 요청 파라미터를 매핑하는 과정에서 에러가 발생했을 시, 즉 Controller에서 @RequestMapping 메소드가 실행되기전에 에러가 발생해도도 ExceptionHandler가 동작할까?

    • ex)@RequestParam int a일 때, a에 String 값을 넘기는 경우.
    • 결론은 동작한다.
  • ExceptionHandler가 동작하는 부분을 찾아보기 위해 DispatcherServlet의 doDispatch() 메소드를 살펴봤다.

    • 아래 메소드를 보면 try ~ catch로 묶인 부분을 보면, request에 대해 handler를 가져와 실행되는 모든 과정에서 exception이 발생하는 경우 catch절에서 잡히게된다.

    • 에러가 발생하여 catch절 온다면 위임할 exception을 담아둔다.
    • 마지막으로 processDispatchResult에서 핸들러 메소드 실행 결과를 처리한다.

protected void doDispatch(HttpServletRequest req, HttpServletResponse res) {  
     try {  
          processedRequest = checkMultipart(req);  
          multipartRequestParsed = (processedRequest != req);  
        
          // 매핑가능한 handler 가져오기  
          mappedHandler = getHandler(processedRequest);  
          ...  
    ​  
          HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
          ...  
    ​  
          // preInterceptor 실행  
          if (!mappedHandler.applyPreHandle(processedRequest, res)) {  
               return;  
          }  
    ​  
          // handler 실행  
          mv = ha.handle(processedRequest, res, mappedHandler.getHandler());  
          ...  
        
          applyDefaultViewName(processedRequest, mv);  
        
          // postInterceptor 실행  
          mappedHandler.applyPostHandle(processedRequest, res, mv);  
     } catch (Exception ex) {  
          dispatchException = ex;  
     } catch (Throwable err) {  
          dispatchException = new NestedServletException("Handler dispatch failed", err);  
     }  
        
     // 핸들러 메소드의 실행 결과 처리  
     processDispatchResult(processedRequest, res, mappedHandler, mv, dispatchException);  
}

- processDispatchResult 메소드에서는 에러가 발생했을때 담아둔 exception이 있는지 확인하고, 있으면 processHandlerException() 메소드에서 처리한다.
private void processDispatchResult(  
     HttpServletRequest request, HttpServletResponse response,  
     @Nullable HandlerExecutionChain mappedHandler,  
     @Nullable ModelAndView mv,  
     @Nullable Exception exception) throws Exception {  
    ​  
     boolean errorView = false;  
        
     if (exception != null) {  
          if (exception instanceof ModelAndViewDefiningException) {  
               logger.debug("ModelAndViewDefiningException encountered", exception);  
               mv = ((ModelAndViewDefiningException) exception).getModelAndView();  
          } else {  
               Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
        
               // 실제 Exception 처리  
               mv = processHandlerException(request, response, handler, exception);  
               errorView = (mv != null);  
          }  
     }   
     ...  
        
    }

- processHandlerException() 에서는 등록된 `this.handlerExceptionResolvers`에 따라 결과를 반환한다.
- 기본적으로 등록되어있는 `this.handlerExceptionResolvers`는 ExceptionHandlerResolver / ResponseStatusExceptionResolver / DefaultHandlerExceptionResolver가 있다.
protected ModelAndView processHandlerException(  
     HttpServletRequest req, HttpServletResponse res,  
     @Nullable Object handler, Exception ex) throws Exception {  
        
     // Check registered HandlerExceptionResolvers...  
     ModelAndView exMv = null;  
        
     if (this.handlerExceptionResolvers != null) {  
          for (HandlerExceptionResolver exceptionResolver : this.handlerExceptionResolvers) {  
               exMv = exceptionResolver.resolveException(req, res, handler, ex);  
               if (exMv != null) {  
                    break;  
               }  
          }  
     }  
     ...  
        
    }

댓글