Spring Framework 5.2.3.RELEASE, Spring Boot 2.2.4.RELEASE 기준으로 작성됨
Spring webmvc를 사용하는 경우에 대한 설명
Spring Framework를 쓰면 @ExceptionHandler를 사용하여 전역 에러 처리를 한다. (기존 작성한 글 참조)
2019/04/30 - [Study/Java] - Spring Boot 전역 에러 처리
제공되는 HandlerExceptionResolver로 처리할 수 없는 경우
@ExceptionHandler annotation을 사용하여 다양한 에러들을 처리할 수 있지만 지정된 Exception이 아닌 경우는 처리를 할 수 없다.
예를 들어 Hystrix를 사용하는 경우 HystrixRuntimeException으로 허용된 Exception을 발생시키는데 이때 실제 발생한 Exception은 HystrixRuntimeException의 cause에 담겨 있게 된다. (cause 변수는 Exception의 최상위 객체인 Throwable에 설정된 변수이고 Exception내에 원인이 되는 Exception을 담기 위해 사용된다.)
HystrixRuntimeException의 cause에 AException이 존재하는 경우 처리하는 @ExceptionHandler와 BExcetpion이 존재하는 경우 처리하는 @ExceptionHandler를 구분하여 관리하고 싶어도 스프링이 기본 제공하는 @ExceptionHandler는 선언된 Exception이 아닌 세부적인 조건에 대한 에러 처리를 제공하지 않는다.
이런 경우 별도의 Custom HandlerExceptionResolver를 선언하여 사용해야 한다.
HandlerExceptionResolverComposite의 동작
이전 글에 설명한 바와 같이 Spring에서 @ExceptionHandler annotation을 통한 전역 에러 처리는 ExceptionHandlerExceptionResolver가 담당하고 있다.
Spring webmvc (Servlet 기반 web)를 사용하면 ExceptionHandlerExceptionResolver 외에 DefaultHandlerExceptionResolver, ResponseStatusExceptionResolver가 자동으로 등록된다.
이 ExceptionResolver들을 순차적으로 실행하여 에러 처리를 하기 위해 HandlerExceptionResolverComposite가 사용된다.
public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
@Nullable
private List<HandlerExceptionResolver> resolvers;
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* Set the list of exception resolvers to delegate to.
*/
public void setExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.resolvers = exceptionResolvers;
}
/**
* Return the list of exception resolvers to delegate to.
*/
public List<HandlerExceptionResolver> getExceptionResolvers() {
return (this.resolvers != null ? Collections.unmodifiableList(this.resolvers) : Collections.emptyList());
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/**
* Resolve the exception by iterating over the list of configured exception resolvers.
* <p>The first one to return a {@link ModelAndView} wins. Otherwise {@code null} is returned.
*/
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (this.resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
}
resolver 목록을 순서대로 실행하여 반환된 MovelAndView 값이 null이면 다음 resolver를 실행하는 간단한 composite 패턴이다.
Custom HandlerExceptionResolver 만들기
별도의 Custom HandlerExceptionResolver를 아래와 같이 만든다.
public class HystrixRuntimeAExceptionHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// HystrixRuntimeException인지 체크
if (!(ex instanceof HystrixRuntimeException)) {
return null;
}
Throwable cause = ((HystrixRuntimeException) ex).getCause();
// cause 변수가 A Exception인지 체크
if (!(cause instanceof AException)) {
return null;
}
AException targetException = (AException) cause;
// 적당히 반환할 값 처리 (Map이 아니어도 상관없음)
Map<String, ErrorMessage> resultMap = new HashMap<>();
resultMap.put("result", "에러 결과 처리");
ModelAndView modelAndView = new ModelAndView("에러페이지 지정", resultMap);
modelAndView.setStatus(HttpStatus.BAD_REQUEST);
return modelAndView;
}
}
HystrixRuntimeException의 cause에 AException이 들어있는 경우 해당 에러에 대한 처리를 반환하도록 한 HandlerExceptionResolver이다.
이렇게 만들어진 custom HandlerExceptionResolver를 등록한다.
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
HandlerExceptionResolver exceptionHandlerExceptionResolver = resolvers.stream().filter(x -> x instanceof ExceptionHandlerExceptionResolver).findAny().get();
int index = resolvers.indexOf(exceptionHandlerExceptionResolver);
resolvers.add(index, new HystrixRuntimeAExceptionHandlerExceptionResolver);
WebMvcConfigurer.super.extendHandlerExceptionResolvers(resolvers);
}
}
WebMvcConfigurer는 HandlerExceptionResolver를 등록하기 위한 메서드를 2개 제공한다.
configureHandlerExceptionResolvers 메서드는 위에서 설명한 Spring webmvc가 기본 등록하는 3개의 HandlerExceptionResolver 대신 새로 등록 처리를 하는 경우 사용하고 extendHandlerExceptionResolvers 메서드는 기본 등록된 HandlerExceptionResolver에 추가하는 경우 사용한다.
이때 기본 등록된 HandlerExceptionResolver 들보다 우선 동작해야 하기 때문에 위와 같이 처리하였다. (그냥 단순히 0번 index로 add 해도 상관없다.)
'Study > Java' 카테고리의 다른 글
Spring Rest Docs response body 한글 깨짐 문제 (mockmvc 설정 문제) (0) | 2020.06.19 |
---|---|
spring reference 문서는 어떻게 만들어질까? (0) | 2020.06.12 |
[troubleshooting] 아직 명확한 해결법을 찾지 못한 Spring Boot web No ServletContext set 에러 현상 (0) | 2020.06.05 |
Spring Boot 2.3 Release Notes (0) | 2020.05.26 |
JDK 14 New Features (0) | 2020.03.30 |
재미로 보는 Spring Project release train naming (0) | 2020.02.07 |
OOP 개발 원칙 (0) | 2020.01.31 |
RestTemplate Generic responseType 사용 (0) | 2020.01.28 |
Reactor 언제 어떤 Operator를 써야 할까? (4) | 2020.01.21 |
java backend developer roadmap (0) | 2020.01.19 |