@ControllerAdvice
public class CustomAdvice {
@ExceptionHandler(ExampleException.class)
public ResponseEntity<ErrorMessage> customHandler (ExampleException e) {
if(e.getCause() instanceof ExampleDetail_1_Exception) {
ErrorMessage errorMessage = new ErrorMessage("custom Error Message");
return ResponseEntity.status(401).body(errorMessage);
}
else if(e.getCause() instanceof Example_Detail_2_Exception) {
ErrorMessage errorMessage = new ErrorMessage("custom Error Message2");
return ResponseEntity.status(402).body(errorMessage);
}
...
else {
ErrorMessage errorMessage = new ErrorMessage("Unhandled Custom Error Message");
return ResponseEntity.status(400).body(errorMessage);
}
}
}
@ControllerAdvice
@ExceptionHandler(ExampleException.class)
@Controller
나 @RestController
가 적용된 Bean 내부에서 발생하는 특정 예외를 하나의 메서드에서 처리해주기 위한 어노테이션e.getCause() instanceof ExampleDetail_1_Exception
e.getCause()
)가 특정 예외 클래스의 instance인지를 체크하여, 상황에 맞게 커스텀된 에러 코드 & 에러 메세지 & status를 반환한다.@ControllerAdvice
어노테이션 인터페이스
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
// ...
}
@ExceptionHandler
어노테이션 인터페이스
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
// ...
}
어노테이션 선언에 사용되는 어노테이션
@Getter
, @Setter
@NonNull
@NonNull
을 넣은 파라미터에 대한 null 체크를 메소드 앞에 삽입해주게 된다. 해당 파라미터에 값을 할당하는 메소드가 있다면, 해당 메소드에도 null 체크 로직을 삽입해준다.@Autowired
그 외에,
@ControllerAdvice
어노테이션 인터페이스
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
// ...
}
@ExceptionHandler
어노테이션 인터페이스
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
// ...
}
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
....
....
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
....
....
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
....
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
...
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
....
}
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
...
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
....
....
throw ex;
}
}
사용자의 요청이 들어오면 디스패처 서블릿은 요청에 알맞은 컨트롤러를 찾아 처리를 요청하는데,
이때 exception이 발생하게 되면 (등록된 ControllerAdivce 빈이 주입된) HandlerExceptionResolver가 익셉션을 잡게 되고,
ControllerAdvice 빈에서 익셉션을 잡아 작성한 ExceptionHandler 클래스 내부의 ExceptionHandler로 넘어가는데,
ExceptionHandler는 명시된 익셉션 클래스에 속하는 익셉션을 잡는다.
exception이 발생했을 때 모듈 내부의 정보(실제 사용중인 주소, DB 이름, 패키지 구조 등)를 사용자에게 숨길 수 있다.
exception을 모아서 관리할 수 있다.
예외 생성 비용을 절감할 수 있다.
의 목적으로 exception handler를 사용해, 발생하는 예외를 잡아 특정 메서드에서 처리해주게 된다.