SpringBoot —— 异常处理
-
概念
- 传统的Java程序都是用try-catch语句捕获异常,而SpringBoot项目采用了全局异常的概念——所有方法均抛出异常,并且专门安排一个类统一拦截并处理这些异常。这样做的好处是可以把用于异常处理的代码单独存放在一个全局异常处理类中。如果未来需要修改异常处理方案,就可以直接在这个全局异常处理类中进行修改
-
拦截异常
-
概念
- 在开发的过程中,如果服务器内部错误会返回500的错误页面或者对应的json数据,但是上线的项目是不允许的,所以开发人员必须对最底层的异常进行拦截
-
拦截特定异常
-
概念
- 为了拦截异常,SpringBoot提供了两个注解,即@ControllerAdvice和@ExceptionHandler
- @ControllerAdvice用于标注类,把被该注解标注的类称作全局异常处理类
- @ExceptionHandler用于标注方法,被该注解标注的方法用于处理特定异常,其value属性是一个Class类型的数组。通过设置value属性,既可以拦截一种类型的异常,又能够拦截多种类型的异常
- 异常处理方法的返回值会作为响应返回给客户端
-
示例
controller.HelloController:
@RestController public class HelloController { @RequestMapping("/hello") public String hello() { int a = 1 / 0; return "Hello World"; } }
exception.ExceptionController:
@ControllerAdvice public class ExceptionController { @ExceptionHandler(ArithmeticException.class) @ResponseBody public String exception(Exception e) { return "0不能作为除数哦"; } }
-
-
拦截全局最底层异常
-
概念
- 当一个SpringBoot项目没有对用户触发的异常进行拦截时,用户触发的异常就会触发最底层的异常。实际开发中,开发人员必须对最底层异常进行拦截
-
示例
controller.HelloController:
@RestController public class HelloController { @RequestMapping("/hello") public String hello(String name) { // 客户端不传对应的参数的话,name默认为null,触发空指针异常 return name.toUpperCase(); } }
exception.GlobalExceptionHandler:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public String handleException(Exception e) { return "我只是一个小小的全局异常处理"; } }
-
-
-
打印异常日志
-
概念
- 我们知道SpringBoot的异常处理都是交由异常处理类来完成的,如果在异常处理方法中写好对应的异常类型参数,那么SpringBoot就可以将具体的异常对象注入用于拦截并处理异常的方法参数中
- 当然,如果要打印异常日志的话,SpringBoot推荐我们使用slf4j
-
示例
controller.ExceptionController:
@RestController public class ExceptionController { @RequestMapping("/index") public String index(@RequestParam String name, @RequestParam Integer age) { return "姓名:"+name+", 年龄:"+age; } }
exception.GlobalExceptionHandler:
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(value = MissingServletRequestParameterException.class) @ResponseBody public String exceptionHandler(MissingServletRequestParameterException e) { logger.error("缺少参数,客户端没有传递!", e); return "你知道异常了嘛?"; } }
-
-
缩小拦截异常的范围
-
概念
- 被@Controller注解标注的类会默认拦截所有被触发的异常
-
拦截由某个或者多个包触发的异常
-
概念
- @ControllerAdvice注解中,有一个value属性,可以接受一个字符串或者多个字符串组成的数组。字符串代表的是包名
- basePackages属性,其功能与value属性相同
-
示例
controller01.IndexController:
@RestController public class IndexController { @RequestMapping("/index") public String index(@RequestParam String name) { return name + ",您好,欢迎来到&尋的站点"; }
controller02.UserController:
@RestController public class UserController { @RequestMapping("/register") public String register(@RequestParam String name) { return "注册成功,您的用户名是:"+name; } }
exception.GlobalExceptionHandler:
@ControllerAdvice("com.kaiven.springbootprintexception.controller02") // 只拦截controller02包中发生的异常 public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) @ResponseBody public String exceptionHandler(Exception e) { return "你知道异常了嘛?"; } }
-
-
拦截由某个或者多个注解标注的类触发的异常
-
概念
- 如果一个SpringBoot项目的所有控制器类都被存储在一个包下,那么通过设置@ControllerAdvice注解的annotations属性,既能够指定拦截由某个注解标注的类触发的异常,又能够指定拦截由多个注解标注的类触发的异常
- 属性值是一个字符串或者字符串数组,字符串代表的是注解名
-
示例
controller.IndexController:
@RestController public class IndexController { @RequestMapping("/index") public String index(@RequestParam String name) { return "你好,"+name; } }
controller.UserController:
@RestController public class UserController { @RequestMapping("/login") public String login(@RequestParam String name) { return "您输入的姓名为:"+name; } }
exception.GlobalExceptionHandler:
@ControllerAdvice(annotations = RestController.class) public class GlobalExceptionHandler { @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseBody public String handler(){ return "你没有传参数啊,大哥"; } }
-
-
-
拦截自定义异常
-
概念
- 功能多、业务多的项目都会编写自定义异常,以便及时处理一些不符合业务逻辑的数据
-
步骤
- 创建自定义异常类,这个类必须继承RuntimeException运行时异常类,并重写父类的构造方法
- 创建全局异常处理类,用于拦截自定义的异常
- 创建控制器类,指定自定义异常的触发条件
-
示例
exception.NegativePriceException:
public class NegativePriceException extends RuntimeException { public NegativePriceException(String message) { super(message); } }
exception.GlobalException:
@ControllerAdvice public class GlobalException { @ExceptionHandler(value = NegativePriceException.class) @ResponseBody public String negativePriceException(NegativePriceException e) { return "你家屋头,价格怎么可能为负数呢?"; } }
controller.ExceptionController:
@RestController public class ExceptionController { @RequestMapping("/index") public String index(Integer price) { if(price == null) return "未输入商品价格"; if(price < 0) throw new NegativePriceException("price is negative"); return "商品的价格是:"+price; } }
-
-
设定自定义异常的错误状态
-
概念
- 在拦截自定义异常时,如果没有编写全局异常处理类,服务器就会返回默认的错误状态
- 使用@ResponseStatus注解即刻设定自定义异常的错误状态
- 该注解的value属性和code属性均可用于设定自定义异常的错误状态
- 配合HttpStatus枚举实现
-
示例
exception.NegativeAgeException:
@ResponseStatus(HttpStatus.BAD_REQUEST) public class NegativeAgeException extends RuntimeException{ @Serial private static final long serialVersionUID = 1L; public NegativeAgeException(String message) { super(message); } }
controller.ExceptionController:
@RestController public class ExceptionController { @RequestMapping("/index") public String index(@RequestParam(defaultValue = "18") Integer age) { if (age < 0) throw new NegativeAgeException("年龄怎么可能是负数呢?"); return "你的年龄是:"+age; } }
-