GVKun编程网logo

springboot 统一异常处理(springboot怎么处理异常)

30

针对springboot统一异常处理和springboot怎么处理异常这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展18、SpringBoot中统一异常处理、SpringBoot中Web应

针对springboot 统一异常处理springboot怎么处理异常这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展18、SpringBoot 中统一异常处理、Spring Boot 中 Web 应用的统一异常处理、spring boot 中统一异常处理、Spring Boot 实践 —— 统一异常处理等相关知识,希望可以帮助到你。

本文目录一览:

springboot 统一异常处理(springboot怎么处理异常)

springboot 统一异常处理(springboot怎么处理异常)

我们在做 Web 应用的时候,请求处理过程中发生错误是非常常见的情况。Spring Boot 提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。我怎么这么有钱

选择一个之前实现过的 Web 应用(Chapter3-1-2)为基础,启动该应用,访问一个不存在的 URL,或是修改处理内容,直接抛出异常,如:

 

1

2

3

4

 

@RequestMapping("/hello")

public String hello() throws Exception {

throw new Exception ("发生错误");

}

此时,可以看到类似下面的报错页面,该页面就是 Spring Boot 提供的默认 error 映射页面。

alt=默认的错误页面​alt = 默认的错误页面

统一异常处理

虽然,Spring Boot 中实现了默认的 error 映射,但是在实际应用中,上面你的错误页面对用户来说并不够友好,我们通常需要去实现我们自己的异常提示。

下面我们以之前的 Web 应用例子为基础(Chapter3-1-2),进行统一异常处理的改造。都市隐龙萧青帝

  • 创建全局异常处理类:通过使用 @ControllerAdvice 定义统一的异常处理类,而不是在每个 Controller 中逐个定义。@ExceptionHandler 用来定义函数针对的异常类型,最后将 Exception 对象和请求 URL 映射到 error.html
 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

 

@ControllerAdvice

class GlobalExceptionHandler {

public static final String DEFAULT_ERROR_VIEW = "error";

@ExceptionHandler(value = Exception.class)

public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

ModelAndView mav = new ModelAndView();

mav.addObject("exception", e);

mav.addObject("url", req.getRequestURL());

mav.setViewName(DEFAULT_ERROR_VIEW);

return mav;

}

}

  • 实现 error.html 页面展示:在 templates 目录下创建 error.html,将请求的 URL 和 Exception 对象的 message 输出。
 

1

2

3

4

5

6

7

8

9

10

11

12

 

<!DOCTYPE html>

<html>

<head lang="en">

<meta charset="UTF-8" />

<title> 统一异常处理 </title>

</head>

<body>

<h1>Error Handler</h1>

<div th:text="${url}"></div>

<div th:text="${exception.message}"></div>

</body>

</html>

启动该应用,访问:http://localhost:8080/hello,可以看到如下错误提示页面。

alt=自定义的错误页面​alt = 自定义的错误页面

通过实现上述内容之后,我们只需要在 Controller 中抛出 Exception,当然我们可能会有多种不同的 Exception。然后在天才鬼医@ControllerAdvice 类中,根据抛出的具体 Exception 类型匹配 @ExceptionHandler 中配置的异常类型来匹配错误映射和处理。

返回 JSON 格式

在上述例子中,通过 @ControllerAdvice 统一定义不同 Exception 映射到不同错误处理页面。而当我们要实现 RESTful API 时,返回的错误是 JSON 格式的数据,而不是 HTML 页面,这时候我们也能轻松支持。

本质上,只需在 @ExceptionHandler 之后加入 @ResponseBody,就能让处理函数 return 的内容转换为 JSON 格式。

下面以一个具体示例来实现返回 JSON 格式的异常处理。

  • 创建统一的 JSON 返回对象,code:消息类型,message:消息内容,url:请求的 url,data:请求返回的数据
 

1

2

3

4

5

6

7

8

9

10

11

12

13

 

public class ErrorInfo<T> {

public static final Integer OK = 0;

public static final Integer ERROR = 100;

private Integer code;

private String message;

private String url;

private T data;

// 省略 getter 和 setter

}

  • 创建一个自定义异常,用来实验捕获该异常,并返回 json
 

1

2

3

4

5

6

7

 

public class MyException extends Exception {

public MyException(String message) {

super(message);

}

}

  • Controller 中增加 json 映射,极品摄影师抛出 MyException 异常
 

1

2

3

4

5

6

7

8

9

 

@Controller

public class HelloController {

@RequestMapping("/json")

public String json() throws MyException {

throw new MyException ("发生错误 2");

}

}

  • MyException 异常创建对应的处理
 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

 

@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(value = MyException.class)

@ResponseBody

public ErrorInfo<String> jsonErrorHandler(HttpServletRequest req, MyException e) throws Exception {

ErrorInfo<String> r = new ErrorInfo<>();

r.setMessage(e.getMessage());

r.setCode(ErrorInfo.ERROR);

r.setData("Some Data");

r.setUrl(req.getRequestURL().toString());

return r;

}

}

  • 启动应用,访问:http://localhost:8080/json,可以得到如下返回内容:
 

1

2

3

4

5

6

 

{

code: 100,

data: "Some Data",

message: "发生错误 2",

url: "http://localhost:8080/json"

}

18、SpringBoot 中统一异常处理

18、SpringBoot 中统一异常处理

1.1、定义统一返回结构

public class JsonResult {  
  
    private int code;  
  
    private String msg;  
  
    public JsonResult() {  
        this.code = 200;  
        this.msg = "成功";  
    }  
  
    public JsonResult(int code, String msg) {  
        this.code = code;  
        this.msg = msg;  
    }  
  
    public static JsonResult ofSuccess() {  
        return new JsonResult();  
    }  
  
    public static JsonResult ofError() {  
        return new JsonResult(500, "错误");  
    }  
  
    public static JsonResult ofError(int code, String msg) {  
        return new JsonResult(code, msg);  
    }  
	
    // 省略get/set等方法
}

1.2、处理异常

@ControllerAdvice  
@ResponseBody  
public class GlobalExceptionHandler {  
  
    @ExceptionHandler(MissingServletRequestParameterException.class)  
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)  
    public JsonResult handleHttpMessageNotReadableException(MissingServletRequestParameterException ex) {  
        System.out.println("缺少请求参数: " + ex.getMessage());  
        return JsonResult.ofError(404, "缺少必要的参数");  
    }  
  
    @ExceptionHandler(NullPointerException.class)  
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)  
    public JsonResult handleTypeMismatchException(NullPointerException ex) {  
        System.out.println("空指针异常: " + ex.getMessage());  
        return JsonResult.ofError(500, "空指针异常了");  
    }  

    // 建议写在GlobalExceptionHandler最下面,如果都没有找到,最后再拦截一下Exception异常,保证输出信息友好  
    @ExceptionHandler(Exception.class)  
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)  
    public JsonResult handleUnexpectedServer(Exception ex) {  
        return new JsonResult(500, "系统发生异常, 请联系管理员");  
    }  
}

1.3、处理自定义异常

public enum BusinessMsgEnum {  
  
  PARMETER_EXCEPTION(102, "参数异常!"),  
  
  SERVICE_TIME_OUT(103, "服务调用超时!"),  

  PARMETER_BIG_EXCEPTION(102, "输入的图片数量不能超过50张!"),  
  
  UNEXPECTED_EXCEPTION(500, "系统发生异常,请联系管理员!");  
  
  private int code;  
  
  private String msg;  
  
  BusinessMsgEnum(int code, String msg) {  
      this.code = code;  
      this.msg = msg;  
  }  
  
  // 省略set/get方法
}
public class BusinessErrorException extends RuntimeException {  
  
    private static final long serialVersionUID = -7480022450501760611L;  
  
    private int code;  

    private String message;  
  
    public BusinessErrorException(BusinessMsgEnum businessMsgEnum) {  
        this.code = businessMsgEnum.getCode();  
        this.message = businessMsgEnum.getMsg();  
    }  
  
    public int getCode() {  
        return code;  
    }  
  
    public void setCode(int code) {  
        this.code = code;  
    }  
  
    @Override  
    public String getMessage() {  
        return message;  
    }  
  
    public void setMessage(String message) {  
        this.message = message;  
    }  
}
@ExceptionHandler(BusinessErrorException.class)  
@ResponseStatus(value = HttpStatus.INTERNAL\_SERVER\_ERROR)  
public JsonResult handleBusinessError(BusinessErrorException ex) {  
    int code = ex.getCode();  
    String message = ex.getMessage();  
    return new JsonResult(code, message);  
}
// 测试Controller
@RestController  
@RequestMapping("/exception")  
public class ExceptionController {  
  
    @GetMapping("/test")  
    public JsonResult test(@RequestParam("name") String name, @RequestParam("pass") String pass) {  
        System.out.println(name + ", " + pass);  
        String s = null;  
        System.out.println(s.length());  
        return JsonResult.ofSuccess();  
    }  
}

Spring Boot 中 Web 应用的统一异常处理

Spring Boot 中 Web 应用的统一异常处理

我们在做 Web 应用的时候,请求处理过程中发生错误是非常常见的情况。Spring Boot 提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。

选择一个之前实现过的 Web 应用(Chapter3-1-2)为基础,启动该应用,访问一个不存在的 URL,或是修改处理内容,直接抛出异常,如:

@RequestMapping("/hello")
public String hello() throws Exception {
    throw new Exception("发生错误");
}

此时,可以看到类似下面的报错页面,该页面就是 Spring Boot 提供的默认 error 映射页面。


alt = 默认的错误页面

统一异常处理

虽然,Spring Boot 中实现了默认的 error 映射,但是在实际应用中,上面你的错误页面对用户来说并不够友好,我们通常需要去实现我们自己的异常提示。

下面我们以之前的 Web 应用例子为基础(Chapter3-1-2),进行统一异常处理的改造。

  • 创建全局异常处理类:通过使用 @ControllerAdvice 定义统一的异常处理类,而不是在每个 Controller 中逐个定义。@ExceptionHandler 用来定义函数针对的异常类型,最后将 Exception 对象和请求 URL 映射到 error.html
@ControllerAdvice
class GlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

}
  • 实现 error.html 页面展示:在 templates 目录下创建 error.html,将请求的 URL 和 Exception 对象的 message 输出。
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8" />
    <title>统一异常处理</title>
</head>
<body>
    <h1>Error Handler</h1>
    <div th:text="${url}"></div>
    <div th:text="${exception.message}"></div>
</body>
</html>

启动该应用,访问:http://localhost:8080/hello,可以看到如下错误提示页面。


alt = 自定义的错误页面

通过实现上述内容之后,我们只需要在 Controller 中抛出 Exception,当然我们可能会有多种不同的 Exception。然后在 @ControllerAdvice 类中,根据抛出的具体 Exception 类型匹配 @ExceptionHandler 中配置的异常类型来匹配错误映射和处理。

返回 JSON 格式

在上述例子中,通过 @ControllerAdvice 统一定义不同 Exception 映射到不同错误处理页面。而当我们要实现 RESTful API 时,返回的错误是 JSON 格式的数据,而不是 HTML 页面,这时候我们也能轻松支持。

本质上,只需在 @ExceptionHandler 之后加入 @ResponseBody,就能让处理函数 return 的内容转换为 JSON 格式。

下面以一个具体示例来实现返回 JSON 格式的异常处理。

  • 创建统一的 JSON 返回对象,code:消息类型,message:消息内容,url:请求的 url,data:请求返回的数据
public class ErrorInfo<T> {

    public static final Integer OK = 0;
    public static final Integer ERROR = 100;

    private Integer code;
    private String message;
    private String url;
    private T data;

    // 省略getter和setter

}
  • 创建一个自定义异常,用来实验捕获该异常,并返回 json
public class MyException extends Exception {

    public MyException(String message) {
        super(message);
    }

}
  • Controller 中增加 json 映射,抛出 MyException 异常
@Controller
public class HelloController {

    @RequestMapping("/json")
    public String json() throws MyException {
        throw new MyException("发生错误2");
    }

}
  • MyException 异常创建对应的处理
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = MyException.class)
    @ResponseBody
    public ErrorInfo<String> jsonErrorHandler(HttpServletRequest req, MyException e) throws Exception {
        ErrorInfo<String> r = new ErrorInfo<>();
        r.setMessage(e.getMessage());
        r.setCode(ErrorInfo.ERROR);
        r.setData("Some Data");
        r.setUrl(req.getRequestURL().toString());
        return r;
    }

}
  • 启动应用,访问:http://localhost:8080/json,可以得到如下返回内容:
{
    code: 100data: "Some Data"message: "发生错误2"url: "http://localhost:8080/json"
}

至此,已完成在 Spring Boot 中创建统一的异常处理,实际实现还是依靠 Spring MVC 的注解,更多更深入的使用可参考 Spring MVC 的文档。

本文完整示例:chapter3-1-6

spring boot 中统一异常处理

spring boot 中统一异常处理

什么是异常?

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

为什么要统一处理异常?

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

异常实例

举个例子,还用之前的学生信息那个案例,我们添加一个小于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(25figuremockmvc注解,配合mockmvcRequestBuilders、mockmvcResultMatchers来测试,具体示例代码如下:

 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,有兴趣的同学自己可以去尝试。

 

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

总结

以上是小编为你收集整理的spring boot 中统一异常处理全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

Spring Boot 实践 —— 统一异常处理

Spring Boot 实践 —— 统一异常处理

注解说明

  @ControllerAdvice,是Spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。让我们先看看@ControllerAdvice的实现:

/**
 * Specialization of {@link Component @Component} for classes that declare
 * {@link ExceptionHandler @ExceptionHandler}, {@link InitBinder @InitBinder}, or
 * {@link ModelAttribute @ModelAttribute} methods to be shared across
 * multiple {@code @Controller} classes.
 *
 * <p>Classes with {@code @ControllerAdvice} can be declared explicitly as Spring
 * beans or auto-detected via classpath scanning. All such beans are sorted via
 * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
 * AnnotationAwareOrderComparator}, i.e. based on
 * {@link org.springframework.core.annotation.Order @Order} and
 * {@link org.springframework.core.Ordered Ordered}, and applied in that order
 * at runtime. For handling exceptions, an {@code @ExceptionHandler} will be
 * picked on the first advice with a matching exception handler method. For
 * model attributes and {@code InitBinder} initialization, {@code @ModelAttribute}
 * and {@code @InitBinder} methods will also follow {@code @ControllerAdvice} order.
 *
 * <p>Note: For {@code @ExceptionHandler} methods, a root exception match will be
 * preferred to just matching a cause of the current exception, among the handler
 * methods of a particular advice bean. However, a cause match on a higher-priority
 * advice will still be preferred to a any match (whether root or cause level)
 * on a lower-priority advice bean. As a consequence, please declare your primary
 * root exception mappings on a prioritized advice bean with a corresponding order!
 *
 * <p>By default the methods in an {@code @ControllerAdvice} apply globally to
 * all Controllers. Use selectors {@link #annotations()},
 * {@link #basePackageClasses()}, and {@link #basePackages()} (or its alias
 * {@link #value()}) to define a more narrow subset of targeted Controllers.
 * If multiple selectors are declared, OR logic is applied, meaning selected
 * Controllers should match at least one selector. Note that selector checks
 * are performed at runtime and so adding many selectors may negatively impact
 * performance and add complexity.
 *
 * @author Rossen Stoyanchev
 * @author Brian Clozel
 * @author Sam Brannen
 * @since 3.2
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {

    /**
     * Alias for the {@link #basePackages} attribute.
     * <p>Allows for more concise annotation declarations e.g.:
     * {@code @ControllerAdvice("org.my.pkg")} is equivalent to
     * {@code @ControllerAdvice(basePackages="org.my.pkg")}.
     * @since 4.0
     * @see #basePackages()
     */
    @AliasFor("basePackages")
    String[] value() default {};

    /**
     * Array of base packages.
     * <p>Controllers that belong to those base packages or sub-packages thereof
     * will be included, e.g.: {@code @ControllerAdvice(basePackages="org.my.pkg")}
     * or {@code @ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"})}.
     * <p>{@link #value} is an alias for this attribute, simply allowing for
     * more concise use of the annotation.
     * <p>Also consider using {@link #basePackageClasses()} as a type-safe
     * alternative to String-based package names.
     * @since 4.0
     */
    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #value()} for specifying the packages
     * to select Controllers to be assisted by the {@code @ControllerAdvice}
     * annotated class.
     * <p>Consider creating a special no-op marker class or interface in each package
     * that serves no purpose other than being referenced by this attribute.
     * @since 4.0
     */
    Class<?>[] basePackageClasses() default {};

    /**
     * Array of classes.
     * <p>Controllers that are assignable to at least one of the given types
     * will be assisted by the {@code @ControllerAdvice} annotated class.
     * @since 4.0
     */
    Class<?>[] assignableTypes() default {};

    /**
     * Array of annotations.
     * <p>Controllers that are annotated with this/one of those annotation(s)
     * will be assisted by the {@code @ControllerAdvice} annotated class.
     * <p>Consider creating a special annotation or use a predefined one,
     * like {@link RestController @RestController}.
     * @since 4.0
     */
    Class<? extends Annotation>[] annotations() default {};

}

该注解使用 @Component 注解,这样的话当我们使用 <context:component-scan> 扫描时也能扫描到。

通过类型的描述,可以得知几点:

  1.     @ControllerAdvice 是一个 @Component,用于定义 @ExceptionHandler,@InitBinder 和 @ModelAttribute 方法,适用于所有使用 @RequestMapping 方法。
  2.     Spring4 之前,@ControllerAdvice 在同一调度的 Servlet 中协助所有控制器。Spring4 已经改变:@ControllerAdvice 支持配置控制器的子集,而默认的行为仍然可以利用。
  3.     在 Spring4 中, @ControllerAdvice 通过 annotations (), basePackageClasses (), basePackages () 方法定制用于选择控制器子集

不过据经验之谈,只有配合 @ExceptionHandler 最有用,其它两个不常用。

如果单使用 @ExceptionHandler,只能在当前 Controller 中处理异常。但当配合 @ControllerAdvice 一起使用的时候,就可以摆脱那个限制了。

实现方式

一、统一异常处理中心(Handler)

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;

/**
 * 统一异常处理
 * @ClassName: GlobalExceptionHandler 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年8月29日 下午2:19:42  
 *
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理所有不可知的异常
     * @Title: handleException 
     * @Description: TODO
     * @Date 2018年8月29日 下午2:21:10 
     * @author OnlyMate
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public JSONObject handleException(HttpServletRequest req, Exception e){
        JSONObject jsonObject = new JSONObject();
Throwable throwable = e.getCause();
        logger.error(throwable.getMessage(), e);
        if (throwable instanceof DuplicateSubmitException) {
         jsonObject.put("resultCode", ((DuplicateSubmitException) throwable).getCode());
jsonObject.put("resultMsg", ((DuplicateSubmitException) throwable).getMsg());

        } else {
            jsonObject.put("resultCode", "9999");
jsonObject.put("resultMsg", "系统异常");

        }
return jsonObject; } /** * 处理自定义异常(这里处理的异常是继承RunTimeException的自定义的异常) * @Title: handleBusinessException * @Description: TODO * @Date 2018年8月29日 下午2:24:55 * @author OnlyMate * @param e * @return */ @ResponseBody @ExceptionHandler(CustomBizRuntimeException.class) public JSONObject handleBusinessException(CustomBizRuntimeException e){ logger.error("handleBusinessException ==> "+ e.getErrorMessage(), e); JSONObject jsonObject = new JSONObject(); jsonObject.put("resultCode", "9999"); jsonObject.put("resultMsg", "CustomBizRuntimeException 自定义业务异常类 ==> 系统异常"); return jsonObject; } /** * 处理自定义异常(这里处理的异常是继承Exception的自定义的异常) * @Title: handleCommonException * @Description: TODO * @Date 2018年8月29日 下午2:24:55 * @author OnlyMate * @param e * @return */ @ResponseBody @ExceptionHandler(CustomCommonException.class) public JSONObject handleCommonException(CustomCommonException e){ logger.error("handleCommonException ==> "+ e.getErrorMessage(), e); JSONObject jsonObject = new JSONObject(); jsonObject.put("resultCode", "9999"); jsonObject.put("resultMsg", "CustomCommonException 自定义普通异常类 ==> 系统异常"); return jsonObject; } }

 

二、自定义异常

CustomBizRuntimeException,继承 RuntimeException,可用于涉及事务操作时抛该异常回滚事务

/**
 * 自定义业务异常类
 * @ClassName: CustomBizRuntimeException 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年9月1日 下午4:17:49  
 *
 */
public class CustomBizRuntimeException extends RuntimeException {
    private static final long serialVersionUID = -8021190492897458166L;
    private String errorMessage = "CustomBizRuntimeException 自定义业务异常类 ";
    
    public String getErrorMessage() {
        return this.errorMessage;
    }
}

CustomCommonException,普通自定义异常

/**
 * 自定义普通异常类
 * @ClassName: CustomCommonException 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018年9月1日 下午4:17:49  
 *
 */
public class CustomCommonException extends Exception {
    private static final long serialVersionUID = -8021190492897458166L;
    private String errorMessage = "CustomCommonException 自定义普通异常类";
    
    public String getErrorMessage() {
        return this.errorMessage;
    }
}

三、编写抛出对应异常的 Controller

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.onlymate.springboot.exception.CustomBizRuntimeException;
import com.onlymate.springboot.exception.CustomCommonException;

@Controller
@RequestMapping(value="/exception")
public class ExceptionController {
    private Logger logger = LoggerFactory.getLogger(HelloWorldController.class);
    
    /**
     * 抛出CustomCommonException异常,然后处理该异常
     * @Title: index1 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:42:58 
     * @author OnlyMate
     * @return
     * @throws CustomCommonException
     */
    @ResponseBody
    @RequestMapping("/index1")
    public String index1() throws CustomCommonException {
        try {
            String str = null;
            logger.info(str.toString());
        } catch (Exception e) {
            throw new CustomCommonException();
        }
        return "Success";
    }
    
    /**
     * 抛出CustomBizRuntimeException异常,然后处理该异常
     * @Title: index2 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:43:25 
     * @author OnlyMate
     * @return
     */
    @ResponseBody
    @RequestMapping("/index2")
    public String index2() {
        try {
            String str = null;
            logger.info(str.toString());
        } catch (Exception e) {
            throw new CustomBizRuntimeException();
        }
        return "Success";
    }
    
    /**
     * 抛出Exception异常,然后处理该异常
     * @Title: index3 
     * @Description: TODO
     * @Date 2018年9月1日 下午4:43:49 
     * @author OnlyMate
     * @return
     */
    @ResponseBody
    @RequestMapping("/index3")
    public String index3() {
        String str = null;
        logger.info(str.toString());
        return "Success";
    }
}

效果图

一、抛出 CustomCommonException 异常效果图

页面访问:http://localhost:8088/springboot/exception/index1

二、抛出 CustomBizRuntimeException 异常效果图

页面访问:http://localhost:8088/springboot/exception/index2

 

三、抛出 Exception 异常效果图

页面访问:http://localhost:8088/springboot/exception/index3

 

 

关于springboot 统一异常处理springboot怎么处理异常的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于18、SpringBoot 中统一异常处理、Spring Boot 中 Web 应用的统一异常处理、spring boot 中统一异常处理、Spring Boot 实践 —— 统一异常处理的相关信息,请在本站寻找。

本文标签: