【自定义异常】SpringBoot自定义异常
1. 前言
在Java中,自定义异常是一种由程序员根据特定需求创建的异常类型。它们可以帮助我们在程序中明确和处理特定的异常情况。以下是一些使用自定义异常的常见应用场景:
-
更好地表示业务逻辑错误:自定义异常可以根据具体业务需求来定义,并提供更有意义和准确描述的错误信息,以便于程序员和其他开发人员理解问题所在。
-
区分不同类型的错误:通过创建多个不同类型的自定义异常类,可以区分并捕获不同类型的错误。例如,如果某个系统需要处理多个网络连接相关的问题,就可以创建一个"NetworkConnectionException"和"TimeoutException"等不同类型的自定义异常。
-
提供更详细的上下文信息:自定义异常可以携带额外的上下文信息,使得异常处理过程更具可读性和可理解性。例如,在数据库操作过程中出现问题时,可以使用自定义异常来携带有关数据库表、字段或查询条件等相关信息。
-
异常处理策略:通过捕获对应的自定义异常,我们可以实施针对不同类型问题的特定处理策略。这有助于提高程序容错性和系统稳定性。
-
API设计:当我们编写库或框架时,通过使用自定义异常来抛出合适的错误信息给客户端用户能够更好地使用API,并能快速诊断和解决问题。
需要注意的是,在使用自定义异常时应遵循一些最佳实践:
- 选择有意义且与问题相关的名称来命名自定义异常类。
- 继承现有合适的Java标准库中已存在的基础异常类(如RuntimeException或Exception)。
- 提供必要且恰当描述错误情况或原因的构造函数、方法和属性。
通过合理使用自定义异常,我们能够更好地管理代码中可能发生的非正常情况,并提供清晰明确、易于调试和维护的代码结构。
2. 自定义异常demo
本文的项目工程为SpringBoot项目,需要依赖有以下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
项目目录结构如下图所示:
2.1 创建自定义异常及全局异常处理器
- 创建自定义异常,继承
RuntimeException
类。
public class MyException extends RuntimeException{
private String errMsg;
public MyException(String message, Throwable cause) {
super(message, cause);
this.errMsg = message;
}
public MyException(String message) {
super(message);
this.errMsg = message;
}
public String getErrMsg() {
return errMsg;
}
}
- 创建全局异常处理类,捕捉自定义异常
MyException
和Exception
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHander {
public static final String INTERNAL_SERVER_ERROR = "服务器内部出错了";
@ExceptionHandler(value={Exception.class})
public ResponseHelper exceptionHandler(Exception e){
log.error("Exception, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(value = {MyException.class})
public ResponseHelper myEeceptionHandler(MyException myException) {
log.error("Exception, errInfo:{}", myException.getErrMsg(), myException);
return ResponseHelper.fail(myException.getErrMsg());
}
}
2.2 创建响应工具类
响应工具类是为了让我们的控制层返回结果更加规范、统一。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseHelper<T> {
public String msg;
public T result;
public String succ;
public static <T>ResponseHelper<T> ok(){
return new ResponseHelper<>(null, null, "ok");
}
public static <T>ResponseHelper<T> ok(String msg){
return new ResponseHelper<>(msg, null, "ok");
}
public static <T>ResponseHelper<T> ok(String msg, T data){
return new ResponseHelper<>(msg, data, "ok");
}
public static <T> ResponseHelper<T> fail(String msg) {
return new ResponseHelper<>(msg, null, "fail");
}
}
2.3、 创建Controller
@RestController
public class ExceptionController {
@Autowired
private ExceptionService exceptionService;
@GetMapping("/test1")
public ResponseHelper test1() {
int i = 10/0;
return ResponseHelper.ok("异常测试1~ 整数除以0");
}
@GetMapping("/ageCheck")
public ResponseHelper ageCheck(@RequestParam Integer age) {
return exceptionService.ageCheck(age);
}
}
2.4 创建Service
- 创建service接口
public interface ExceptionService {
ResponseHelper ageCheck(Integer age);
}
- 创建实现类
@Service
public class ExceptionServiceImpl implements ExceptionService {
@Override
public ResponseHelper ageCheck(Integer age) {
if (age < 0) {
throw new MyException("年龄参数小于0,请重新提交");
}
return ResponseHelper.ok("test3方法执行完毕");
}
}
3. 测试
- 访问
http://localhost:8080/test1
结果如下:
可以看出,由于test1方法中有整数除以0,导致抛出了一个异常,被全局异常处理类捕捉到后并返回了一个消息。 - 访问
http://localhost:8080/ageCheck?age=-1
结果如下:
可以看出,由于提价的年龄参数小于0,被全局异常类捕捉到MyException并返回了响应的信息。
4. 通用的一个全局异常捕捉类
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHander {
public static final String INTERNAL_SERVER_ERROR = "服务器内部出错了";
public static final String REQUEST_NOT_SUPPORT_METHOD = "请求方法不支持";
public static final String BODY_NOT_MATCH = "POST参数不匹配";
public static final String REQUEST_NOT_MATCH = "请求参数不匹配";
@ExceptionHandler(value={Exception.class})
public ResponseHelper exceptionHandler(Exception e){
log.error("Exception, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(value = {MyException.class})
public ResponseHelper myEeceptionHandler(MyException myException) {
log.error("Exception, errInfo:{}", myException.getErrMsg(), myException);
return ResponseHelper.fail(myException.getErrMsg());
}
/**
* 参数不合法异常
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseHelper methodArgumentNotValidException(MethodArgumentNotValidException e){
String errMsg = e.getBindingResult().getAllErrors().stream().map(item -> item.getDefaultMessage()).collect(Collectors.joining());
return ResponseHelper.fail(errMsg);
}
/**
* POST缺少body参数
* @param e
* @return
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseHelper httpMessageNotReadableHandler(HttpMessageNotReadableException e) {
log.error("HttpMessageNotReadableException, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(BODY_NOT_MATCH);
}
/**
* 请求方法不正确
**/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseHelper httpRequestMethodNotSupportedHandler(HttpRequestMethodNotSupportedException e) {
log.error("HttpRequestMethodNotSupportedException, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(REQUEST_NOT_SUPPORT_METHOD);
}
/**
* 未传入参数异常 @NotBlank
**/
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseHelper missingServletRequestParameterHandler(MissingServletRequestParameterException e) {
log.error("MissingServletRequestParameterException, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(REQUEST_NOT_MATCH);
}
/**
* 缺少请求参数
**/
@ExceptionHandler(BindException.class)
public ResponseHelper bindExceptionHandler(BindException e) {
log.error("BindException, errInfo:{}", e.getMessage(), e);
return ResponseHelper.fail(REQUEST_NOT_MATCH);
}
}