加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

spring boot 中统一异常处理

发布时间:2020-12-15 01:09:54 所属栏目:大数据 来源:网络整理
导读:什么是异常? 通俗的说就是,让你感觉不爽的,阻碍你的事都算异常,也就是说不让我们程序正常运行的情况。 为什么要统一处理异常? 方便集中管理,集中定位问题 异常实例 举个例子,还用之前的学生信息那个案例,我们添加一个小于18岁的学生,调用接口,控制

什么是异常?

通俗的说就是,让你感觉不爽的,阻碍你的事都算异常,也就是说不让我们程序正常运行的情况。

为什么要统一处理异常?

方便集中管理,集中定位问题

异常实例

举个例子,还用之前的学生信息那个案例,我们添加一个小于18岁的学生,调用接口,控制台报错如下:

?

?

?再看接口返回信息,如下图:

? ? ?

?

?

?

?

?添加失败? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 添加成功

?

?

暂且先不说控制台报错,对比下,我们添加成功的接口信息返回情况,明显这给客户端调用我们程序的同学,有些不便,那么我们这里做下优化。

1、统一格式化输出json

?强迫症的我,这里有必要做下统一格式的输出,那么具体怎么做呢?

增加一个外层对象,用于包裹里面对象,具体代码示例如下:

package com.rongrong.springboot.demo.domain;

import lombok.Data;

/**
 * @description: 最外层对象
 * @author rongrong
 * @version 1.0
 * @date 2020/1/9 21:51
 */
@Data
public class Result<T> {
    private Integer code;
     String msg;
     T data;
}

针对成功、失败,定制统一的工具类,具体示例代码如下:

 com.rongrong.springboot.demo.utils;

 com.rongrong.springboot.demo.domain.Result;


 * @description: 统一格式化输出json
 *  1.0
 * @date 2020/1/9 21:55
 */
class ResultUtils {

    static Result success(Object obj){
        Result result = new Result();
        result.setCode(0);
        result.setMsg("success");
        result.setData(obj);
        return result;
    }

     Result success(){
        return success(null);
    }

     Result error(String msg){
        Result result =  Result();
        result.setCode(-1);
        result.setMsg(msg);
        //result.setMsg("unknown error");
         result;
    }
}

接着我们需要对添加学生的接口进行改造,将我们封装好的工具类引入,达到统一输出的效果,具体代码如下:

 
     * 新增一个学生
     *
     * @return
     
    @PostMapping("/studentAdd")
    public Result<Student> sudentAdd(@Valid Student student,BindingResult bindingResult) {
        if(bindingResult.hasFieldErrors()){
            Result result = ResultUtils.error(bindingResult.getFieldError().getDefaultMessage());
            输出错误信息
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
             result;
        }
        student.setName(student.getName());
        student.setAge(student.getAge());
        student.setSex(student.getSex());
        student.setEmail(student.getEmail());
        Result result = ResultUtils.success(studentResponstory.save(student));
        保存和更新都用该方法
         result;
    }

我们调用接口服务,再来看接口返回,如下图:

? ? ?

?

?

?再来看下,明显舒服好多了。

2、多个异常情况的统一

现在我们实现这样一组功能,获取学生的年龄并判断,小于10岁,返回“应该上小学”,大于10岁且小于16岁,返回“应该上初中了”

我们需要在StudentService中写逻辑,供controller调用,具体代码如下:

   
     * 查询学生年龄
     *
     * @param id
     * @throws Exception
     */
    void getStudnetAge(Integer id) throws Exception {
        Student student = studentResponstory.findOne(id);
        Integer age = student.getAge();
        小于10岁,返回“应该上小学”,大于10岁且小于16岁,返回“应该上初中了”
        if (age <= 10) {
            throw new Exception("应该上小学");
        } else if (age > 10 && age < 16);
        }
    }

接着我们在StudentController中调用,具体代码示例如下:

   
     * 获取学生年龄
     * 
    @GetMapping("/students/getAge/{id}"void getAge(@PathVariable("id") Integer id)  Exception {
        studentService.getStudnetAge(id);
    }

数据库中学生的信息如下:

我们先来查询id为13、15、16的学生,查看接口返回信息如下:

? ??

? ? ?

?异常不一样,我们需要再次进行统一化管理了,输出统一格式化后的json。

3、使用 @ControllerAdvice 实现全局异常处理

显然我们需要把message中的信息及code组合外部对象,在包装内部返回data对象,这时需要我们使用 @ControllerAdvice 进行全局异常处理,配合@ExceptionHandle注解使用,@ExceptionHandle注解可以自动捕获controller层出现的指定类型异常,并对该异常进行相应的异常处理。

我们先来建立一个统一的异常类,继承RuntimeException,因为对于spring boot框架中,只有RuntimeException类的异常才会进行事务回滚,具体示例代码如下:

 com.rongrong.springboot.demo.exception;


 *  1.0
 * @description:
 * @date 2020/1/10 0:24
 class StudentException extends RuntimeException{
    code码
    错误信息
     String msg;

    public StudentException(Integer code,String msg) {
        this.code = code;
        this.msg = msg;
    }

    void setCode(Integer code) {
         code;
    }

     setMsg(String msg) {
         Integer getCode() {
         String getMsg() {
         msg;
    }
}

注意:此处必须用getSet方法,不能lombok插件,否则会报错,没有定义getSet方法。

接着我们再来编写全局异常处理,并针对异常类型做出判断,具体示例代码如下:

 com.rongrong.springboot.demo.handle;

 com.rongrong.springboot.demo.domain.Result;
 com.rongrong.springboot.demo.exception.StudentException;
 com.rongrong.springboot.demo.utils.ResultUtils;
 org.springframework.web.bind.annotation.ControllerAdvice;
 org.springframework.web.bind.annotation.ExceptionHandler;
 org.springframework.web.bind.annotation.ResponseBody;


 * @description: 全局异常处理
 *  1.0
 * @date 2020/1/10 0:17
 
@ControllerAdvice
 ExceptionHandle {

    @ResponseBody
    @ExceptionHandler(Exception. Result error(Exception e) {
        if(e instanceof StudentException){
            StudentException studentException=(StudentException)e;
             ResultUtils.error(studentException.getCode(),studentException.getMsg());
        }else {
            return ResultUtils.error(-1,"unknown error!");
        }
    }
}

同样的,我们需要对StudentService中作出调整,修改为我们自定义的异常,具体示例代码如下:

  new StudentException(100,"应该上小学"new StudentException(101,"应该上初中了");
        }
    }

重新启动项目,再次调用查询学生年龄接口,查看返回结果如下所示证明成功。

? ?

?

?

?4、对code码的统一管理维护

很明显,现在两个报错对应两个code和msg,那么如果有多种code和msg对应的话这里感觉维护起来就很难了,所以我们要把它拿出来统一集中管理就好,这里使用枚举,来实现code和msg的映射。

具体示例代码如下:

 com.rongrong.springboot.demo.exceptionenum;

 1.0
 * @description:
 * @date 2020/1/9 23:11
 */

enum ResultEnum {

    UNKNOW_ERROR(-1,1)">),HIGH_SCHOOL(10001,"应该上小学啦!");
     msg;
    }

    ResultEnum(Integer code,1)"> msg;
    }
}

接下来,需要我们在对StudentService中作出调整,修改为我们自定义的异常,传参为我们的枚举对象,具体示例代码如下:

小于10岁,返回“应该上小学”,大于10岁且小于16岁,返回“应该上初中了”,其他正常输出
         StudentException(ResultEnum.PRIMARY_SCHOOL);
        }  StudentException(ResultEnum.HIGH_SCHOOL);
        } StudentException(ResultEnum.SUCCESS);
        }
    }

接着在对,StudentException这个异常构造器,做下调整,具体代码如下:

 com.rongrong.springboot.demo.exceptionenum.ResultEnum;

 StudentException(ResultEnum resultEnum) {
         resultEnum.getCode();
         resultEnum.getMsg();
    }

     msg;
    }
}

最后,我们再来启动项目,调用下接口,返回如下信息,证明修改成功!

? ?

?5、单元测试

为了程序能够更好的运行,我们必须要做测试,所以要养成写完程序进行单元测试的好习惯。

那么在这里我们需要对Service、API进行测试。

5.1、对service进行单元测试

可以通过自定义创建类,来编写单元测试,也可以通过idea向导来创建,具体操作如下图所示:

具体示例代码如下:

 com.rongrong.springboot.demo.controller;

 com.rongrong.springboot.demo.domain.Student;
 com.rongrong.springboot.demo.responstory.StudentResponstory;
 org.junit.Assert;
 org.junit.Test;
 org.junit.runner.RunWith;
 org.springframework.beans.factory.annotation.Autowired;
 org.springframework.boot.test.context.SpringBootTest;
 org.springframework.test.context.junit4.SpringRunner;


 * @description: 对service进行单元测试
 *  1.0
 * @date 2020/1/10 20:52
 
@RunWith(SpringRunner.)
@SpringBootTest
 StudentControllerTest {

    @Autowired
    StudentResponstory studentResponstory;

    @Test
     sudentFindOne() {
        Student student = studentResponstory.findOne(13);
        Assert.assertEquals(new Integer(25
 org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 org.springframework.test.context.junit4.SpringRunner;
 org.springframework.test.web.servlet.MockMvc;
 org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 org.springframework.test.web.servlet.result.MockMvcResultMatchers;


 * @description: 对API进行单元测试
 *  1.0
 * @date 2020/1/10 21:12
 )
@SpringBootTest
@AutoConfigureMockMvc
 StudentApiTest {


    @Autowired
    MockMvc mockMvc;

    @Test
    void testStudentApiTest()  Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/students"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("student"));
    }

}

运行测试,结果如下:

?

到此,spring boot 中统一异常处理,AutoConfigureMockMvc这个注解,感觉与powermock很像,其中各种APi,有兴趣的同学自己可以去尝试。

?

学习他人的优点,对比自己的不足!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读