SpringBoot-统一异常处理

来源:互联网 发布:一个人开两家淘宝店 编辑:程序博客网 时间:2024/06/06 03:01

SpringBoot-统一异常处理

一、什么是异常处理

异常(exception) 是程序运行过程中发生的事件, 该事件可以中断程序指令的正常执行流程.

1当Java程序运行时出现问题时,系统会自动检测到该错误,并立即生成一个与该错误对应的异常对象2:然后把该异常对象提交给JAVA虚拟机3:JAVA虚拟机会自动寻找相应的处理代码来处理这个异常,如果没有找到,则程序终止4:程序员可以自己编写代码来扑捉可能出现的异常,并编写代码来处理相应的异常.

二、为什么要统一异常处理

例:需求:

获取某女生的年龄并判断:小于10,返回“应该在上小学”;大于10且小于16,返回“可能在上初中”;

需求:把System.out.println(bindingResult.getFieldError().getDefaultMessage());信息输出到网页

版本一:

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    //添加一个女生    @PostMapping(value="/girls")    public Girl girlAdd(@Valid Girl girl, BindingResult bindingResult) {        if (bindingResult.hasErrors()) {            System.out.println(bindingResult.getFieldError().getDefaultMessage());            return null;        }        girl.setCupSize(girl.getCupSize());        girl.setAge(girl.getAge());        return girlRepository.save(girl);    }}

Girl:

@Entity //该注解表示该类在数据库中有对应的表 不用创建该表public class Girl {    @Id    @GeneratedValue    private Integer id;    @NotBlank(message = "这个字段必传")    private String cupSize;    /**     * 给年龄加上限制:年龄必须大于18岁     * value:值     * message:提示信息     */    @Min(value =18,message = "年龄必须大于18岁")    private Integer age;    @NotNull(message = "金额必传")    private Double money;    public Girl() {    }    @Override    public String toString() {        return "Girl{" +                "id=" + id +                ", cupSize='" + cupSize + '\'' +                ", age=" + age +                '}';    }    public Double getMoney() {        return money;    }    public void setMoney(Double money) {        this.money = money;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getCupSize() {        return cupSize;    }    public void setCupSize(String cupSize) {        this.cupSize = cupSize;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }}

1.jpg

2.jpg

目前,金额必传是打印在控制台上的,现在需要打印在网页上:

修改方法:

版本二:

@PostMapping(value="/girls")public Object girlAdd(@Valid Girl girl, BindingResult bindingResult) {    if (bindingResult.hasErrors()) {        return bindingResult.getFieldError().getDefaultMessage();    }    girl.setCupSize(girl.getCupSize());    girl.setAge(girl.getAge());    return girlRepository.save(girl);}

结果:

3.jpg

4.jpg

问题:返回的格式不统一,对返回的格式作统一(失败和成功):

{    "code":1,    "msg":"金额必传",    "data":null}{    "code":0    "msg":"成功",    "data":{        "id":20,        "cupSize":"B",        "age":25,        "money":1.2    }}

版本三:

Result:

/** * Created by HCX on 2017/8/17. * http请求返回的最外层对象 */public class Result<T> {    //错误码    private Integer code;    //提示信息    private String msg;    //具体的内容    private T data;    public Integer getCode() {        return code;    }    public void setCode(Integer code) {        this.code = code;    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public T getData() {        return data;    }    public void setData(T data) {        this.data = data;    }}

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    //添加一个女生    @PostMapping(value="/girls")    public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {        if(bindingResult.hasErrors()){            Result result = new Result();            result.setCode(1);            result.setMsg(bindingResult.getFieldError().getDefaultMessage());            result.setData(null);            return result;        }        girl.setCupSize(girl.getCupSize());        girl.setAge(girl.getAge());        Result result = new Result();        result.setCode(0);        result.setMsg("成功");        result.setData(girlRepository.save(girl));        return result;    }}

运行结果:

5.jpg

6.jpg

可见代码重复了,进行优化:版本四

定义返回结果的工具类ResultUtil:

public class ResultUtil {    public static Result success(Object object){        Result result = new Result();        result.setCode(0);        result.setMsg("成功");        result.setData(object);        return result;    }    public static Result success(){        return success(null);    }    public static Result error(Integer code,String msg){        Result result = new Result();        result.setCode(code);        result.setMsg(msg);        return result;    }}

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    //添加一个女生    @PostMapping(value="/girls")    public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {        if(bindingResult.hasErrors()){            return ResultUtil.error(1,bindingResult.getFieldError().getDefaultMessage());        }        girl.setCupSize(girl.getCupSize());        girl.setAge(girl.getAge());        return ResultUtil.success(girlRepository.save(girl));    }}

三、案例

需求:获取某女生的年龄并判断,小于10,返回“应该在上小学”;大于10且小于16,返回“可能在上初中”

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    @GetMapping(value = "girls/getAge/{id}")    public void getAge(@PathVariable("id") Integer id) throws Exception{        girlService.getAge(id);    }}

GirlService:

@Servicepublic class GirlService {    @Autowired    private GirlRepository girlRepository;    public void getAge(Integer id) throws Exception{        Girl girl = girlRepository.findOne(id);        Integer age = girl.getAge();        if(age<10){            //返回“你可能在上小学” code=100            throw new Exception("你可能在上小学");        }else if(age>10 && age<16){            //返回“你可能在上初中” code=101            throw new Exception("你可能在上初中");        }    }}

ExceptionHandle:

@ControllerAdvicepublic class ExceptionHandle {    /**     *  @ExceptionHandler(value = Exception.class):声明要捕获的异常类     * @return     */    @ExceptionHandler(value = Exception.class)    @ResponseBody    public Result handle(Exception e){        return ResultUtil.error(100,e.getMessage());    }}

此时,已经按照我们所期望的格式返回了:

7.jpg

问题:返回的错误码都是一样的,但是在实际问题中,我们希望定位的错误更准确,希望不同的错误可以返回不同的错误码,所以可以自定义一个Exception:

GirlException:

public class GirlException extends RuntimeException{    /**     * 注意要继承自RuntimeException,底层RuntimeException继承了Exception,     * spring框架只对抛出的异常是RuntimeException才会进行事务回滚,     * 如果是抛出的是Exception,是不会进行事物回滚的     */    private Integer code;    public GirlException(Integer code,String message) {        //父类的构造方法本身会传message进去        super(message);        this.code = code;    }    public Integer getCode() {        return code;    }    public void setCode(Integer code) {        this.code = code;    }}

ExceptionHandle:

@ControllerAdvicepublic class ExceptionHandle {    /**     *  @ExceptionHandler(value = Exception.class):声明要捕获的异常类     * @return     */    @ExceptionHandler(value = Exception.class)    @ResponseBody    public Result handle(Exception e){        if(e instanceof GirlException){            GirlException girlException = (GirlException)e;            return ResultUtil.error(girlException.getCode(),girlException.getMessage());        }else {            return ResultUtil.error(-1,"未知错误");        }    }}

GirlService:

@Servicepublic class GirlService {    @Autowired    private GirlRepository girlRepository;    public void getAge(Integer id) throws Exception{        Girl girl = girlRepository.findOne(id);        Integer age = girl.getAge();        if(age<10){            //返回“你可能在上小学” code=100            throw new GirlException(100,"你可能在上小学");        }else if(age>10 && age<16){            //返回“你可能在上初中” code=101            throw new GirlException(101,"你可能在上初中");        }    }}

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    @GetMapping(value = "girls/getAge/{id}")    public void getAge(@PathVariable("id") Integer id) throws Exception{        girlService.getAge(id);    }}

运行结果:

8.jpg

9.jpg

当抛出的是系统异常时,可以通过日志进行记录:

10.jpg

ExceptionHandle:

@ControllerAdvicepublic class ExceptionHandle {    //记录系统异常    private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);    /**     *  @ExceptionHandler(value = Exception.class):声明要捕获的异常类     * @return     */    @ExceptionHandler(value = Exception.class)    @ResponseBody    public Result handle(Exception e){        if(e instanceof GirlException){            GirlException girlException = (GirlException)e;            return ResultUtil.error(girlException.getCode(),girlException.getMessage());        }else {            logger.error("【系统异常】 {}",e);            return ResultUtil.error(-1,"未知错误");        }    }}

问题:在自定义异常的错误码和信息时,如果过多,没有统一管理,则会出现重复。使用枚举统一管理code和message:

ResultEnum:

public enum ResultEnum {    UNKNOW_ERROR(-1,"未知错误"),    SUCCESS(0,"成功"),    PRIMARY_SCHOOL(100,"你可能在上小学"),    MIDDLE_SCHOOL(101,"你可能在上初中"),    ;    private  Integer code;    private String msg;    ResultEnum(Integer code, String msg) {        this.code = code;        this.msg = msg;    }    public Integer getCode() {        return code;    }    public String getMsg() {        return msg;    }}

GirlException:

public class GirlException extends RuntimeException{    /**     * 注意要继承自RuntimeException,底层RuntimeException继承了Exception,     * spring框架只对抛出的异常是RuntimeException才会进行事务回滚,     * 如果是抛出的是Exception,是不会进行事物回滚的     */    private Integer code;    public GirlException(ResultEnum resultEnum) {        //父类的构造方法本身会传message进去        super(resultEnum.getMsg());        this.code = resultEnum.getCode();    }    public Integer getCode() {        return code;    }    public void setCode(Integer code) {        this.code = code;    }}

GirlService:

@Servicepublic class GirlService {    @Autowired    private GirlRepository girlRepository;    public void getAge(Integer id) throws Exception{        Girl girl = girlRepository.findOne(id);        Integer age = girl.getAge();        if(age<10){            //返回“你可能在上小学” code=100            throw new GirlException(ResultEnum.PRIMARY_SCHOOL);        }else if(age>10 && age<16){            //返回“你可能在上初中” code=101            throw new GirlException(ResultEnum.MIDDLE_SCHOOL);        }    }}

GirlController:

@RestControllerpublic class GirlController {    /**     * getLogger方法中的参数与类名对应     */    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);    @Autowired    private GirlRepository girlRepository;    @Autowired    private GirlService girlService;    @GetMapping(value = "girls/getAge/{id}")    public void getAge(@PathVariable("id") Integer id) throws Exception{        girlService.getAge(id);    }}
原创粉丝点击