@LEELEE
dev note
@LEELEE
  • 분류 전체보기 (29)
    • Project (10)
    • Study (19)
      • Java (9)
      • Spring (3)
      • Test (2)
      • DB (2)
      • Infra (1)
      • ETC (2)
      • CS (0)
    • 노트 (0)

인기 글

최근 글

태그

  • 배포자동화
  • HTTP
  • 예외처리
  • PS
  • 이펙티브자바
  • was
  • 객체지향
  • test
  • AWS
  • oracle
  • springboot
  • Spring
  • PostgreSQL
  • junit
  • transaction
  • DB
  • Java
  • Til
  • Redis
  • Leetcode
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
@LEELEE

dev note

Study/Java

@ExceptionHandler는 어떻게 예외를 처리할 수 있을까?

2023. 7. 20. 02:51

 

결론부터 말하자면 DispatcherServlet 덕분에 가능하다.
DispatcherServlet의 동작 방식을 확장하여 만들어진 여러 가지 전략 중 예외 처리 전략도 있다.

  • HandlerMapping(URL과 요청정보를 기준으로 어떤 컨트롤러를 사용할 건지 결정)
  • HandlerAdapter(HandlerMapping 전략에 따라 선택한 컨트롤러가 어떤 타입이든 DispatcherServlet가 사용할 수 있게 함)
  • ViewResolver(컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아줌)
  • ...
  • HandlerExceptionResolver(예외 처리)

예외가 발생했을 때 종류에 따라 클라이언트에게 알려주는 작업은 DispatcherServlet을 통해 처리되어야 하기 때문이다.

 

@Controller
public class HelloCon {
	@RequestMapping("/hello")
	public void hello() {
		if (1==1) throw new DataRetrievalFailureException("hi");
	}

	// *DataRetrievalFailureException는 DataAccessException의 자식 클래스
	@ExceptionHandler(DataAccessException.class)
	public ModelAndView dataAccessExceptionHandler(DataAccessException ex) {
		return new ModelAndView("dataexception").addObject("msg", ex.getMessage());
	}
}

 

 

 

DispatcherServlet에서 예외 처리까지

public class DispatcherServlet extends FrameworkServlet {
     ...
     protected void doDispatch(HttpServletRequest request ...) {
        ...
        try {
            try {
                Exception dispatchException = null;

                try {
                    ...
                    // 컨트롤러 로직 실행
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    ...
                    
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
                
                // 1. 디스패치 결과 처리
                // 위에서 잡은 dispatchException 넘김 
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
			
            	...
    }
    
    // 1. 디스패치 결과 처리
    private void processDispatchResult(HttpServletRequest request ...) {
            	...
                // 2. 핸들러 예외 처리
                mv = this.processHandlerException(request, response, handler, exception);
                ...
            }
        }
        ...
     }
     
    // 2. 핸들러 예외 처리
    @Nullable
    protected ModelAndView processHandlerException(HttpServletRequest request ...) {
        ...
        ModelAndView exMv = null;
        if (this.handlerExceptionResolvers != null) {
            Iterator var6 = this.handlerExceptionResolvers.iterator();
            // 전략마다 예외 처리
            while(var6.hasNext()) {
                // 3. HandlerExceptionResolver 전략의 resolveException 메서드 실행
                HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
                exMv = resolver.resolveException(request, response, handler, ex);
                ...
            }
        }
        ...
    }
...

// 3. HandlerExceptionResolver 전략의 resolveException 메서드 실행
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
    public ModelAndView resolveException(HttpServletRequest request ...) {
            ...
            ModelAndView result = this.doResolveException(request, response, handler, ex);
            
        }
    }

 

 

 

HandlerExceptionResolver

HandlerExceptionResolver 인터페이스

 

HandlerExceptionResolver 인터페이스의 구현체들

AbstractHandlerExceptionResolver를 상속받은 네 개 중 세 개가 디폴트 전략이다.

  • ExceptionHandlerExceptionResolver (AbstractHandlerMethodExceptionResolver 상속받음)
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver

ExceptionHandlerExceptionResolver가 바로 @ExceptionHandler를 사용하기 위한 클래스다.

 

 

AbstractHandlerMethodExceptionResolver 추상 클래스

아까 doDispatch에서부터 이어지던 doResolveException 메서드가 있다.

doResolveException는 doResolveHandlerMethodException 추상 메서드를 호출한다.

 

 

 

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver(Spring 3.2 이전엔 AnnotationMethodHandlerExceptionResolver)는 @ExceptionHandler 어노테이션으로 메서드를 통해 예외를 해결하는 전략이다.

 

https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html

 

ExceptionHandlerExceptionResolver (Spring Framework 4.3.9.RELEASE API)

Invoked by a BeanFactory after it has set all bean properties supplied (and satisfied BeanFactoryAware and ApplicationContextAware). This method allows the bean instance to perform initialization only possible when all bean properties have been set and to

docs.spring.io

 

 

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver implements ApplicationContextAware, InitializingBean {
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request ...) {
            // 4. 예외에 적절한 메서드 찾기
            ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
            ...
            ArrayList<Throwable> exceptions = new ArrayList();

            try {
				...
                Throwable cause;
                for(Throwable exToExpose = exception; exToExpose != null; exToExpose = cause != exToExpose ? cause : null) {
                    exceptions.add(exToExpose);
                    cause = ((Throwable)exToExpose).getCause();
                }

                Object[] arguments = new Object[exceptions.size() + 1];
                exceptions.toArray(arguments);
                arguments[arguments.length - 1] = handlerMethod;
                // 5. 예외 메서드 호출 위임
                exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);

            ...
        }
    }
    
    // 4. 적절한 예외 메서드 찾기
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
        Class<?> handlerType = null;
        if (handlerMethod != null) {
            handlerType = handlerMethod.getBeanType();
            // ExceptionHandlerExceptionResolver가 아닌 별도의 ExceptionHandlerMethodResolver 클래스
            ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)this.exceptionHandlerCache.get(handlerType);
            if (resolver == null) {
                resolver = new ExceptionHandlerMethodResolver(handlerType);
                this.exceptionHandlerCache.put(handlerType, resolver);
            }
            
            // 예외에 매핑된 메서드 찾아서 반환
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
            }
        }
        
        // @ControllerAdvice 사용 시
        Iterator var9 = this.exceptionHandlerAdviceCache.entrySet().iterator();
        while(var9.hasNext()) {
            Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry = (Map.Entry)var9.next();
            ControllerAdviceBean advice = (ControllerAdviceBean)entry.getKey();
            if (advice.isApplicableToBeanType(handlerType)) {
                ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)entry.getValue();
                Method method = resolver.resolveMethod(exception);
                if (method != null) {
                    return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
                }
            }
        }

        return null;
    }

 

 

 

 

참고

<토비의 스프링 3.1>

 

저작자표시 비영리 (새창열림)

'Study > Java' 카테고리의 다른 글

Custom Exception: 검사 예외, 런타임 예외 무엇을 써야할까  (0) 2023.07.21
자바의 함수형 프로그래밍 전략, 메서드 참조  (0) 2023.05.04
[Java] 제네릭  (0) 2023.02.01
JVM 명세 - Run-Time Data Areas  (0) 2022.06.25
자바로 간단한 http 웹 서버 구현  (0) 2022.06.11
    'Study/Java' 카테고리의 다른 글
    • Custom Exception: 검사 예외, 런타임 예외 무엇을 써야할까
    • 자바의 함수형 프로그래밍 전략, 메서드 참조
    • [Java] 제네릭
    • JVM 명세 - Run-Time Data Areas
    @LEELEE
    @LEELEE

    티스토리툴바