Spring Boot提供了强大的异常处理机制,允许开发者捕获和处理应用程序中发生的各种异常,以提供更友好的错误消息或执行适当的错误处理操作。以下是Spring Boot异常处理机制的简要介绍:
@ControllerAdvice
:@ControllerAdvice
是一个注解,它允许您在全局范围内定义异常处理器。异常处理器可以捕获在控制器中抛出的异常并执行相应的操作,如返回错误响应或记录错误日志。
@ExceptionHandler
:@ExceptionHandler
注解用于在控制器或@ControllerAdvice
类中定义方法,以处理特定类型的异常。您可以为不同的异常类型定义多个@ExceptionHandler
方法,以提供特定的异常处理逻辑。
默认异常处理器:Spring Boot提供了一个默认的异常处理器,它会处理未被其他@ExceptionHandler
方法捕获的异常。这样,您可以创建通用的异常处理方法,用于处理未被特定方法处理的异常。
自定义异常类:通常,开发人员会创建自定义异常类,以表示特定应用程序内的异常情况。这些自定义异常类可以被抛出,并在异常处理器中进行捕获和处理。
全局错误页面:Spring Boot还支持自定义全局错误页面,这些页面可以用于显示友好的错误消息或自定义错误页面,以提高用户体验。
使用ResponseEntity
:异常处理器通常返回ResponseEntity
对象,以灵活地控制HTTP响应,包括响应状态码、响应头和响应正文。
如果我们不进行局部异常处理的话,异常的处理实际上是由 Tomcat 来执行的,弹出的页面风格和我们网页也可能不同,展示效果很不友好。
局部异常处理机制是指在特定代码块或方法内部处理异常,而不是将异常传递给调用方或在全局范围内处理异常。这种机制有一些优点和用途:
精确处理:局部异常处理允许您在异常发生的地方执行特定的处理逻辑,而无需将异常传递给调用方。这可以帮助您更精确地处理异常情况,而不必担心全局异常处理可能会处理不相关的异常。
增加可读性:通过在特定代码块内处理异常,您可以将异常处理逻辑与正常业务逻辑分开,提高了代码的可读性和可维护性。开发人员可以更容易地理解和维护代码。
减少错误传播:有些异常可能是暂时性的,可以在方法内部处理,然后继续正常执行。如果将这些异常传递给调用方,可能会导致错误传播,使代码更难以管理和调试。
定制化处理:不同的方法或代码块可能需要不同的异常处理逻辑。局部异常处理机制允许您为每个代码块定义特定的处理方法,以满足其需求。
快速反应:某些异常可能需要即时处理,例如回滚数据库事务或释放资源。局部异常处理可以让您快速响应这些情况,而不必等待全局异常处理器的执行。
先演示一个异常的出现
@Controller
public class myExceptionHandler {
@ResponseBody
@RequestMapping("/Exception/{num}")
public String exceptionTest(@PathVariable("num")int num) {
int x = 10 / num;
return "success";
}
}
这里我们创建一个 Controller 来接收请求,并且从浏览器中获取一个数字对象,将这个数字对象作为变量用到方法中,这时候如果传输进来的是 0 就会抛出一个异常,
下面我们来看一下如果不对这个异常做任何处理的话,会有什么效果:
这是 SpringBoot 默认的一个异常处理界面,这时候如果我们想自己来控制页面的跳转和处理方式的话就可以采用下面的方法
例如我们来对上面的异常信息来做一个简单的返回
@Controller
public class myExceptionHandler {
@ResponseBody
@RequestMapping("/Exception/{num}")
public String exceptionTest(@PathVariable("num")int num) {
int x = 10 / num;
return "success";
}
@ResponseBody
@ExceptionHandler(ArithmeticException.class)
public String localException(Exception e) {
return "异常信息为" + e.getMessage();
}
}
这样我们再次启动服务器并且传递 0 这个参数就会显示我们的处理机制
这样我们就完成了一个简单的局部异常处理,即异常在控制器中立刻被完成处理。
全局异常处理机制的好处包括:
- 集中式异常处理:可以将所有异常处理逻辑集中到一个地方,提高了代码的可维护性。
- 统一错误响应:可以为应用程序定义一致的错误响应格式,提供更好的用户体验。
- 避免异常泄漏:如果未在局部代码块中捕获异常,全局异常处理机制可以防止异常信息泄漏到客户端,增加安全性。
- 灵活性:可以根据需要定义多个异常处理方法,以处理不同类型的异常,为每种异常类型提供特定的处理逻辑。
全局异常处理机制是一种在应用程序范围内捕获和处理异常的方法,通常用于处理未在局部代码块中处理的异常。在Spring Boot等框架中,全局异常处理机制通常是使用
@ControllerAdvice
和@ExceptionHandler
注解来实现的。
// 全局异常不管是哪个 Handler 抛出的异常都可以进行处理
@ResponseBody
@ExceptionHandler
public String globalException(Exception e, HttpServletRequest httpServletRequest) {
return "全局异常处理器";
}
在Spring Boot中使用 @ExceptionHandler
注解而不指定参数,它将处理所有未被更具体的 @ExceptionHandler
方法处理的异常,需要注意的是局部异常是优先于全局异常的,如果我们的异常被局部异常处理器处理的话就不会再交给全局异常处理器去处理。
这时候我们将原本的局部异常处理器注释掉,再去抛出一个错误的话就会利用全局异常处理了
系统提供的异常不足以满足我们的业务需求,这时候根据业务逻辑就需要定义自己的异常,这就是自定义异常。
比如对应下面的场景:我们需要让用户输入自己的年龄,且要求这个年龄区间位于 1 - 120 岁,如果超出这个年龄我们就抛出一个年龄异常,下面来看一下具体的实现
// 设置信息和状态码
@ResponseStatus(reason = "年龄需要在 1 - 120 之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException{
// 存储异常信息
public AgeException(String message) {
super(message);
}
}
@ResponseBody
@RequestMapping("ageException/{age}")
public String ageException(@PathVariable int age) {
if (age > 120 || age < 1) {
throw new AgeException(MessageConstant.AGE_EXCEPTION);
}
return "年龄为 " + age;
}
@ExceptionHandler
@ResponseBody
public String AgeException(AgeException ageException) {
return "年龄异常" + ageException.getMessage();
}
在正常的业务场景中,我们会定义统一的异常处理器,并且会将异常分类处理,比如上面的 AgeException 就可以归类为业务异常,这时候我们定义一个业务异常类比如为 BaseException 让这个 AgeException 去继承这个类,就可以通过在异常处理器中加入参数的形式来实现对不同的异常进行处理
package com.sky.handler;
import com.sky.constant.MessageConstant;
import com.sky.exception.BaseException;
import com.sky.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
/**
* 捕获 sql 异常
* @param ex 抛出的异常
* @return 处理的结果,是已经约定好的
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {
String message = ex.getMessage();
if (message.contains("Duplicate entry")) {
String[] s = message.split(" ");
String username = s[2];
String msg = username + MessageConstant.ALREADY_EXISTS;
return Result.error(msg);
} else {
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
}
比如这里的全局异常处理器就分别对 SQL 异常和业务异常进行不同的处理。