Spring MVC의 예외처리 ExceptionHandler
@ExceptionHandler
어노테이션은 예외처리함수임을 명시한다
괄호안에는 어떤 타입의 예외가 발생했을 경우 처리하는 함수인지 명시한다.
이벤트 함수처럼, 특정 타입의 예외가 발생하면 해당 함수를 호출하여 실행한다.
parseInt는 NumberFormatException을 throw하는 함수이다.
그래서 정수로변환할 수 없는 문자열을 받을 경우 예외가 터진다.
각각의 코드에서 예외가 발생할 수 있는 코드들이 분산되어있을 경우 일일이 예외처리를 해준다면
코드의 가독성이 떨어지고 비슷한 코드들이 반복된다.
SpringMVC에서는 똑같은 예외가 발생할 수 있는 코드들을 한데에 모아서 처리하게 된다.
각각의 코드에 try-catch를 해주는 대신에 Controller에다가 어노테이션 처리를 해준다.
서비스, 컴포넌트, DAO 등에서 발생하는 예외를 컨트롤러로 넘겨서
컨트롤러에서 취합하여 특정 타입의 예외를 처리하는 함수를 별도로 처리한다.
Controller
@ExceptionHandler(NumberFormatException.class)
public ModelAndView num1Exception(NumberFormatException e) {
ModelAndView mav = new ModelAndView("number-exception");
mav.addObject("msg", e.getMessage());
return mav;
}
number-excption. jsp 생성
서버를 실행하면 예외가 터졌을 때 메세지를 ExceptionHandler가 처리하여
모델엔드뷰가 number-exception.jsp로 반환하고
number-exception.jsp에서 예외메세지를 띄운다.
NullPointerException을 처리해보자
public int getNum2() {
String s1 = null;
String s2 = "a";
// return Integer.parseInt("300000000000");
return s1.compareTo(s2);
}
controller
@ExceptionHandler(NullPointerException.class)
public ModelAndView np2() {
ModelAndView mav = new ModelAndView("number-exception");
mav.addObject("msg", "알수없는 문제가 발생했습니다.");
return mav;
}
하지만 이렇게 controller에서 일일이 Handler처리를 해주면
요청과 응답을 처리하는 Controller의 코드가 복잡해진다.
ControllerAdvice: 스프링 빈으로 등록 필수
예외처리함수가 모여있는 클래스에 지정한다.
범위를 별도로 지정하지 않으면, 프로젝트 전역에서 발생하는 예외를 처리한다.
새로운 패키지 생성
root-context로 가서 component-scan을 추가해준다.
Ex02Controller에서 배열의 범위를 초과하는 예외를 터트려보자
@Controller
public class Ex02Controller {
private int[] arr = {4,8,2,7,6};
@GetMapping("/ex02")
public ModelAndView ex02() {
ModelAndView mav = new ModelAndView();
mav.addObject("num1",arr[0]);
mav.addObject("num2",arr[2]);
mav.addObject("num3",arr[5]); //ArrayIndexOutOfBoundsException
return mav;
}
}
exception패키지의 Ex02ControllerAdvice클래스에서 @ControllerAdvice어노테이션을 추가해준다.
assignableTypes :
예외처리함수가 모여있는 클래스에 지정한다.
범위를 별도로 지정하지 않으면, 프로젝트 전역에서 발생하는 예외를 처리한다.
범위를 지정하는 방법
1) assignableTypes : 클래스 정보의 배열을 전달한다.
2) value : 특정 패키지를 문자열로 전달하여 내부의 컨트롤러에서 발생하는 예외를 처리하게된다.
3) annotations : 단일 어노테이션클래스 정보를 전달한다.
@ResponseBody는 따로 jsp를 만들지 않고 함수가 반환하는 내용 그자체가 응답으로 jsp 포워딩하지않는다.
@ControllerAdvice(assignableTypes = {Ex02Controller.class})
// 예외처리함수가 모여있는 클래스에 지정한다.
// 범위를 별도로 지정하지 않으면, 프로젝트 전역에서 발생하는 예외를 처리한다.
// 범위를 지정하는 방법
// 1) assignableTypes : 클래스 정보의 배열을 전달한다.
// 2) annotations : 단일 클래스 정보를 전달한다.
// 3) 문자열 전달 : 특정 패키지 내부의 컨트롤러에서 발생하는 예외를 처리하게된다.
public class Ex02ControllerAdvice {
@ExceptionHandler(ArrayIndexOutOfBoundsException.class)
@ResponseBody //이 함수가 반환하는 내용 그자체가 응답이다. (그러니까 jsp포워딩 하지마라 )
public String indexException(ArrayIndexOutOfBoundsException e ) {
String html = "";
html +="<!DOCTYPE html>";
html +="<html>";
html +="<head></head>";
html +="<body>";
html +="<script>";
html +=" alert('" + e.getMessage() + "')\n";
html +=" location.href = '/day10'";
html +="</script>";
html +="</html>";
return html;
}
}
ex02예외처리를 누르면
만약 service에서 예외를 발생시키지않으면
@Service
public class Ex02Service {
private int[] arr = {4,8,2,7,6};
public int getNum4() { /* throws ArrayIndexOutOfBoundsException */
return arr[1];
}
정상적으로 ex02.jsp가 뜬다.
예외처리는 초반에 만들지 않는 것을 권장한다. 무슨 오류가 발생했는지 알 수 없어서
만드는 과정이 길어지고 복잡해진다.