Java中的全局异常处理


全局异常处理

  • ErrorController接口@ControllerAdvice注解比较:

    • 相同点:
      • 两者都是Spring项目中的全局异常处理方式
    • 不同点:

      • 捕获异常位置不同:

        • ErrorController接口捕获全局所有的异常,包括控制器方法中抛出的异常
        • @ControllerAdvice注解只能捕获控制器方法中抛出的异常 .@Controller注解无法捕获Filter, 拦截器,请求路径等位置抛出的异常

      • 实现方式不同:

        • ErrorController接口是基于跳转页面的.如果有异常发生,就会跳转到 /error页面
        • @ControllerAdvice是基于AOP


    • 如果项目中两者同时存在,那么 @ControllerAdvice注解就处理控制器方法抛出的异常 ,Controller接口就处理未进入控制器方法的异常

SpringBoot的全局异常处理


  • SpringBoot中全局异常默认处理方式是接口ErrorController的方式:

    • 跳转/error路径:

      • 如果请求发生异常 ,Tomcat中的StandardHostValve类的custom方法会重定向到 /error路径
      • 请求将会交由SpringMVC中的DispatcherServlet类进行处理

    • 定位BasicErrorController:
      • DispatcherServlet类将 /error请求和BasicErrorController中的方法进行对应

        • 如果是html的请求方式 ,/error定位到errorHtml() 方法
        • 如果是json的请求方式 ,/error定位到error() 方法


  • SpringBoot中自定义全局异常处理类:
    • SpringBoot默认的错误页不够友好,可以自定义异常处理类,继承BasicErrorController类,重写BasicController类中的errorHtml() 方法和error() 方法

@RestController
public class CustomErrorController extends BasicErrorController {

@Autowired
public CustomErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}

@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
ModelAndView modelAndView = new ModelAndView("customeErrorPage", model, status);
return modelAndView;
}

@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));

Map<String, Object> responseBody = new HashMap<>(8);
responseBody.put("success", false);
responseBody.put("code", body.get("status"));
responseBody.put("message", body.get("error"));

return new ResponseEntity<>(responseBody, HttpStatus.OK);
}
}

@ControllerAdvice注解


  • @ControllerAdvice注解的执行顺序
    Java中的全局异常处理_java
  • 根据请求的执行顺序可知:


    • @ControllerAdvice注解可以捕获搭配aspectcontroller方法中的异常
    • @ControllerAdvice注解不能捕获到filterinterceptor方法中中的异常


  • 使用 @ControllerAdvice注解和 @ExceptionHandler处理异常:

/**
* 自定义异常
*/
@Data
public class ServiceException extends RuntimeException {

private Integer code;

public ServiceException(Integer code, String message) {
super(message);
this.code = code;
}
}
/**
* 处理异常
*/
@Slf4j
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler {

@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result handleException(ServciceException exception) {
log.error(exception.getMessage());
return Result.buildFailure(exception.getCode(), exception.getMessage());
}
}
@Data
public class User {

@NotBlank(message = "用户姓名必须提交!")
private String name;
}
/**
* 异常处理实例
*/
@Controller
@RequestMapping("/exception")
public class MethodExceptionController {

@GetMapping("/user")
@ResponseBody
public Result getName(@Valid @RequestParam User user) {
return Result.buildSuccess(user);
}
}