异常解析器
doDispatch:执行请求的分发
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
try{
//SpringMVC执行流程
...
// 这里执行控制器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
}
catch (Exception ex) {
dispatchException = ex; //当控制器中抛出异常时,会跳转到这里
}
catch (Throwable err) {
...
}
//这里进行结果的处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
...
}
processDispatchResult:执行控制器返回结果的处理
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果控制器发生异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//获取处理器
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//processHandlerException方法用于处理异常
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
...
}
processHandlerException:处理异常
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
//对handlerExceptionResolvers进行遍历,当执行第二个元素时,进行异常的处理
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
...
throw ex;
}
HandlerExceptionResolverComposite类的resolveException:对异常解析器进行遍历,每个解析器依次对异常进行处理
@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;
}
三个异常解析器:
ExceptionHandlerExceptionResolver:解析@ExceptionHandler
ResponseStatusExceptionResolver:解析@ResponseStatus
DefaultHandlerExceptionResolver:如果Spring自带的异常没被以上两个解析器处理,则使用默认处异常解析器处理
@ExceptionHandler
该注解标注的方法用于处理该控制器中抛出的指定异常
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserList")
public List<User> getUserList(){
int i = 1/0; //这里抛出异常
List<User> users = userService.list(null);
return users;
}
//@ExceptionHandler标注的方法用于处理该控制器中抛出的指定异常
@ExceptionHandler(value = {ArithmeticException.class})
public void handleException(Exception e){
System.out.println(e.getMessage());
};
}
=====output:
http://localhost:8080/user/getUserList请求后,控制台打印:
/ by zero
# 相当于handleException处理(try-catch)了这个控制器中抛出的异常
统一处理异常
- @ControllerAdvice默认对所有Controller的抛出异常进行处理。basePackages可以指定要拦截的Controller
- @RestControllerAdvice = @ControllerAdvice + @ResponseBody
- 该注解本质也是个Component,被标注的类会加入ioc容器
/**
* 统一异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* @ExceptionHandler指定出现什么异常时执行这个方法。
* 全局异常处理:@ExceptionHandler中写Exception.class,表示只要出现异常,就会执行该方法
* @param e
* @return CommonResponseVo是自定义的同一返回格式
*/
@ExceptionHandler(Exception.class)
public CommonResponseVo error(Exception e){
System.out.println("全局异常处理...");
return CommonResponseVo.onfailed(e.getMessage());
}
/**
* 特定异常处理
* 当异常发生时,先找特定异常,如果没有,则向上找父类,一直找到Exception.class的全局异常处理方法
* @param e
* @return
*/
@ExceptionHandler(ArithmeticException.class)
public CommonResponseVo error(ArithmeticException e){
System.out.println("特定异常ArithmeticException处理...");
return CommonResponseVo.onfailed(e.getMessage());
}
/**
* 自定义异常处理 需要在异常处try-catch,然后抛出自定义异常
* @param e
* @return
*/
@ExceptionHandler(GuliException.class)
public CommonResponseVo error(GuliException e){
System.out.println("自定义异常GuliException处理...");
return CommonResponseVo.onfailed(e);
}
}
@ResponseStatus
带有@ResponseStatus注解的异常类会被ResponseStatusExceptionResolver 解析。
一般标注在自定义的异常类上,并且抛出该异常时可以显示@ResponseStatus中定义的属性值
1. 自定义异常
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "用户未找到!")
public class UserNotFoundException extends RuntimeException {
}
2. Controller抛出自定义异常
@GetMapping("/login/{username}/{password}")
public String login(@PathVariable String username,@PathVariable String password){
//抛出异常
if(!"admin".equals(username)){
throw new UserNotFoundException();
}
return "success";
}
显示的错误信息会根据@ResponseStatus的value变化,reason属性的值没显示,不知道为啥