SpringMVC为我们提供了异常的统一处理机制,有以下四种方式。需要注意的是如果我们想使用统一的异常处理机制,那么就不要将需要统一处理的异常进行try…catch…处理,而是统一的交给异常处理器处理。
一、利用SpringMVC为我们提供的异常处理器
SpringMVC中定义了一个HandlerExceptionResolver接口,用来统一处理异常。
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex);
}
SpringMVC为我们提供了HandlerExceptionResolver接口的几个实现类,我们可以直接利用这些实现类进行异常的统一处理。
下面以SimpleMappingExceptionResolver为例,说一下这种方式下异常的统一处理:
①自定义异常:
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyException(String message) {
super(message);
}
}
②在SpringMVC的容器配置文件中配置异常处理器:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 默认的异常展示页面:当抛出的异常不是exceptionMappings中特别指定的异常类型时返回该页面 -->
<property name="defaultErrorView" value="error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 需要特殊处理的异常列表:key为异常的类名或完全路径名,值为抛出该异常时的展示页面 -->
<property name="exceptionMappings">
<props>
<!-- 这里配置的是自定义的异常 -->
<prop key="com.bdm.exceptions.MyException">error1</prop>
<prop key="com.bdm.exceptions.MyException1">error2</prop>
</props>
</property>
</bean>
Tip
:
1️⃣异常处理页面的位置须在配置的视图解析器的前缀目录下
2️⃣在页面获取异常信息时使用的变量和exceptionAttribute属性的值要一致,比如在error1.jsp中获取异常信息
<span style="color: red">${ex.message}</span>
二、自定义HandlerExceptionResolver接口的实现
除了直接使用SpringMVC为我们提供的异常处理器,我们也可以自定义异常处理器。自定义异常处理器的方式,能够是我们设置和获取到更丰富的异常信息,步骤如下:
①自定义异常处理器类:实现HandlerExceptionResolver接口
public class MyExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
Map<String, Object> model = new HashMap<String, Object>();
// 定义页面获取异常信息的变量
model.put("ex", ex);
// 这里我们可以放置更多的异常信息
// 根据不同异常转向不同页面
if (ex instanceof MyException) {
return new ModelAndView("error1", model);
} else if (ex instanceof MyException1) {
return new ModelAndView("error2", model);
} else {
return new ModelAndView("error", model);
}
}
}
②在SpringMVC的容器中配置自定义的异常处理器
<bean class="com.bdm.handlers.MyExceptionHandler" />
③在Controller中只要抛出了相应的异常,就会按照对应的异常处理方式进行处理
三、在Controller中使用@ExceptionHandler
我们可以在每个Controller类中增加一个或多个使用@ExceptionHandler注解的方法,当然也可以抽取一个公共父类,将该处理方法在父类中定义:
@RestController
public class TestController {
@RequestMapping("/testException/{id}")
public String testException(@PathVariable Integer id) {
if (id == 1)
throw new MyException(">>>>exception");
return "success";
}
@ExceptionHandler
public String exp(HttpServletRequest request, Exception ex) {
request.setAttribute("ex", ex);
request.setAttribute("msg", "yichang");
// 根据不同错误转向不同页面
if (ex instanceof MyException) {
return "error1";
} else if (ex instanceof MyException1) {
return "error2";
} else {
return "error";
}
}
}
Tip
:
1️⃣@ExceptionHandler注解的方法的优先级高于自定义的HandlerExceptionResolver,该Controller中出现的异常如果在@ExceptionHandler注解的方法中得到了处理就不会再被HandlerExceptionResolver的子类拦截了
2️⃣@ExceptionHandler注解的方法的返回值和该类的定义有关,比如此处类上标注了@RestController,则方法的返回值将是对象本身而不再是页面的地址了
3️⃣这种方式的异常处理的局限性在于只能处理该Controller中抛出的异常,而不能全局处理,当然也可以抽取一个公共父类来定义@ExceptionHandler注解的方法
4️⃣在@ExceptionHandler中可以指定处理的异常
@ExceptionHandler(RuntimeException.class)
public String exp(HttpServletRequest request, Exception ex) {
request.setAttribute("ex", ex);
request.setAttribute("msg", "yichang");
// 根据不同错误转向不同页面
if (ex instanceof MyException) {
return "error1";
} else if (ex instanceof MyException1) {
return "error2";
} else {
return "error";
}
}
四、使用@ControllerAdvice
上面介绍的@ExceptionHandler的方式只能处理当前Controller中抛出的异常,SpringMVC还为我们提供了@ControllerAdvice,可以监控指定包下的Controller中抛出的异常,步骤如下:
①定义相关切面:指定切面作用的包
@ControllerAdvice(basePackages = "com.bdm.controllers")
public class MyControllerAdvice {
@ExceptionHandler(MyException.class)
public ModelAndView myExceptionHandler(MyException ex) {
ex.printStackTrace();
Map<String, Object> map = new HashMap<>();
map.put("ex", ex);
return new ModelAndView("error1", map);
}
}
②在SpringMVC的容器中注册切面类
<bean class="com.bdm.handlers.MyControllerAdvice" />
③在Controller中抛出相应的异常:会被切面方法拦截处理
@RestController
public class TestController {
@RequestMapping("/testException/{id}")
public String testException(@PathVariable Integer id) {
if (id == 1)
throw new MyException(">>>>exception");
return "success";
}
}
Tip
:
1️⃣@ControllerAdvice切面类的处理优先级低于HandlerExceptionResolver的实现类
2️⃣@ControllerAdvice标注的方法并不是只能进行异常处理,它其实可以监控所有Controller中的所有方法,具体可参考Spring MVC之@ControllerAdvice