文章目录

  • 一、引入ErrorController和@ControllerAdivce
  • 二、SpringBoot默认处理异常方式ErrorController
  • 2.1制造异常
  • 2.2处理异常原理
  • 1.跳转/error路径
  • 2.定位到BasicErrorController
  • 三、自定义全局异常处理类MyErrorController
  • 四、@ControllerAdivce捕捉异常
  • 4.1@ControllerAdivce的执行顺序
  • 4.2@ControllerAdivce+@ExceptionHandler处理异常
  • 4.3请求方法
  • 4.4测试结果


代码已上传gitee:https://gitee.com/shang_jun_shu/springboot-exception-jsr303.git 其他系列博客
Java异常处理(一)一异常处理流程java异常处理(二)—从字节码层面看throw关键字及try…catch…finally的实现java异常处理(四)—还在用if判断校验参数?试试SpringBoot全局异常+JSR303校验吧!

一、引入ErrorController和@ControllerAdivce

共同点:

两者都是Spring项目中的异常处理方式,@ControllerAdivce属于Spring的Web模块,ErrorController接口属于SpringBoot项目

区别:

捕捉异常位置不同:@ControllerAdivce捕捉控制器方法中抛出的异常,像Filter、拦截器的异常是捕捉不到的,ErrorController接口捕捉全局异常,包括控制器方法中抛出的异常
实现方式不同:@ControllerAdivce基于aop,ErrorController基于跳转页即如果有异常发生,会跳转/error页面

注意:如果项目中两者同时存在,@ControllerAdivce注解处理控制器方法中抛出的异常,ErrorController处理未进入控制器方法的异常

二、SpringBoot默认处理异常方式ErrorController

2.1制造异常

首先制造一个未进入控制器方法的异常

最常见的就是404路径找不到

java全局Attribute java全局异常处理器_spring boot


再制造一个控制器方法抛出的异常,如下代码

@GetMapping("/get")
@ResponseBody
public Result getEx(){
    throw new RuntimeException();
}

java全局Attribute java全局异常处理器_spring boot_02

2.2处理异常原理

1.跳转/error路径

一旦请求发生异常,Tomcat中StandardHostValve类的custom方法会重定向到/error路径,并且交给SpringMVC中的DispatcherServlet类去处理

2.定位到BasicErrorController

DispatcherServlet类将/error与BasicErrorController中的方法对应起来,如果用户请求方式是HTML,则定位到errorHtml方法,如果请求方式是json,则定位到error方法

三、自定义全局异常处理类MyErrorController

SpringBoot默认的错误页看着不怎么好看,我们可以自定义异常处理类,继承ErrorController,重写该类的errorHtml和error两个方法

@RestController
public class MyErrorController extends BasicErrorController {

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

    //处理html请求
    @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("myErrorPage", model, status);
        return modelAndView;
    }

    //处理json请求
    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        
        //可以换成项目中自定义的通信json
        Map<String,Object> resultBody=new HashMap<>(16);
        resultBody.put("success",false);
        resultBody.put("code",body.get("status"));
        resultBody.put("msg",body.get("error"));

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

对于html请求可以自定义html页,下面看下json请求结果吧

{
    "msg": "Method Not Allowed",
    "code": 405,
    "success": false
}

四、@ControllerAdivce捕捉异常

4.1@ControllerAdivce的执行顺序

java全局Attribute java全局异常处理器_java全局Attribute_03


从上图可以知道@ControllerAdivce可以捕捉到aspect和controller方法中的异常

4.2@ControllerAdivce+@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 handlerException(ServiceException e){
        log.error(e.getMessage());
        return Result.buildFailure(e.getCode(),e.getMessage());
    }
}

4.3请求方法

@Controller
@RequestMapping("/exception")
public class MethodExceptionController {

    @GetMapping("/get")
    @ResponseBody
    public Result getName(@RequestParam  String userName){
        if(StringUtils.isEmpty(userName)){
            throw new ServiceException(10010,"参数错误");
        }
        return Result.buildSuccess(userName);
    }
}

对于参数的校验可以不用if判断,用jsr303即可,参考下一篇

4.4测试结果

http://localhost:8081/exception/get?userName=


{
    "success": false,
    "code": 10010,
    "msg": "参数错误",
    "data": null
}