前言:

  spring boot学习以及使用也有一段时间了,平时疏于整理资料,故今日写一篇关于全局异常的资料

背景:

  异常处理是为了给用户带来良好的交互体验

异常:

  • 访问了错误的页面,或者是非法的访问导致服务器不能返回正常的数据,例如访问了不存在的页面导致404
  • 程序代码内部的错误,在开发时期由于没有考虑周全导致的程序异常,列如常见的空指针异常(NullPointException

操作:

  关于第一种处理:

  可以定义一个ErrorController来进行全局的处理,如果404则会跳转到404.html页面,这种处理会根据statusCode返回相对应的页面,当然这里也可以设置返回成json的数据格式,只要在方法上加上@ResponseBody,然后修改返回值即可.

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;


@Controller
class MainsiteErrorController implements ErrorController {

    @RequestMapping("/error")
    public String handleError(HttpServletRequest request) {
        System.out.println("成功拦截异常信息");
        //获取statusCode:401,404,500
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == 401) {
            return "/error.html";
        } else if (statusCode == 404) {
            return "/404.html";
        } else if (statusCode == 403) {
            return "/error.html";
        } else if (statusCode == 500) {
            return "/error.html";
        } else {
            return "/error.html";
        }
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }

}

关于第二种处理:

  可以定义一个GlobalExceptionHandler全局异常处理类,在类上加@ControllerAdvice异常拦截注解,在方法上面加入@ExceptionHandler(对应的异常类.class)注解,这种方法可以是多个的,Exception是全局的异常处理,如果有ArithmeticException的异常则会优先跳入对应的ArithmeticException处理方法中,则不会进入Exception处理方法中,

import com.example.advanced.result.ResponseCode;
import com.example.advanced.result.ServerResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)
    private String ArithmeticException(HttpServletRequest request, Exception e) {
        System.out.println("ArithmeticException成功拦截");
        return "/error.html";
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    private ServerResponse<Object> handlerErrorInfo(HttpServletRequest request, Exception e) {
        System.out.println("GlobalException成功拦截");
        log.error("{} Exception", request.getRequestURI(), e);
        if (e instanceof BusinessException) {
            return ServerResponse.createByErrorCodeMessage(((BusinessException) e).getCode(), e.getMessage());
        } else {
            return ServerResponse.createByErrorMessage("服务器异常,请联系管理员处理");
        }
    }

}

  值得一提的是我们可以自定义一些异常,列如上述代码中的BusinessException就是业务异常,通过继承RuntimeException类,然后对于一些可能会发生逻辑错误的异常进行手动抛出,然后让全局异常进行判断,[e instanceof BusinessException]这一块内容,如果是业务逻辑异常,则对其进行相对应的处理.

import com.example.advanced.result.ResponseCode;
public class BusinessException extends RuntimeException {

    private Integer code;

    public BusinessException(ResponseCode responseCode) {
        super(responseCode.getDesc());
        this.code = responseCode.getCode();
    }

    public BusinessException(ResponseCode responseCode, String message) {
        super(message);
        this.code = responseCode.getCode();
    }

    //省略get,set
}

总结:

  运行时异常返回的状态码是500,但是会优先被GlobalExceptionHandler拦截,而对应的异常像上文中ArithmeticException(例如int i = 1/0)除数不能为0的异常,会优先被ArithmeticException()这个方法所捕获.

  总的来说异常执行顺序是 ArithmeticException>Exception>errorController