一、为什么要统一处理异常?
在一个大型的项目中,进行统一异常处理是一个很有必要的操作,有以下几个原因:
-
一致的错误响应:通过统一异常处理,可以确保应用程序在遇到异常时返回一致的错误响应。这有助于提供一致的用户体验,无论是在前端客户端还是其他服务端调用者。
-
避免重复代码:在应用程序的各个层级都可能发生异常,如果每个地方都去处理异常,会导致代码重复和冗余。通过统一异常处理,可以将异常处理的逻辑集中在一个地方,避免重复代码的编写。
-
提高代码可维护性:将异常处理逻辑集中在一个地方,可以使代码更易于维护和修改。如果需要更改异常处理的逻辑,只需要修改统一异常处理器,而不需要在应用程序的各个地方进行修改。
-
异常信息的记录和监控:通过统一异常处理,可以方便地记录异常信息,例如日志记录。这样可以帮助开发人员在调试和排查问题时更容易地追踪异常的发生。此外,可以对异常进行监控和分析,从而及时发现和解决潜在的问题。
-
提升用户体验:统一异常处理可以提供更友好和有意义的错误信息,以及适当的错误码。这可以帮助用户更好地理解问题,并提供相应的解决方案或提示。
总而言之,进行统一异常处理有助于提高代码的可维护性、减少冗余代码、提供一致的用户体验,并方便异常信息的记录和监控。这是一个良好的开发实践,可以提升项目的质量和可靠性。
二、注解
当实现统一异常处理时,我们使用了一些注解来实现特定的功能。以下是对这些注解的简要解释:
-
@ControllerAdvice
注解:
@ControllerAdvice
是一个用于标记全局异常处理器类的注解。当你在Spring Boot应用程序中使用该注解时,它会将标记的类识别为全局异常处理器,并使其生效。在全局异常处理器类中,你可以定义多个@ExceptionHandler
方法来处理不同类型的异常。 -
@ExceptionHandler
注解:
@ExceptionHandler
是一个用于标记异常处理方法的注解。通过在全局异常处理器类中定义多个@ExceptionHandler
方法,你可以为不同类型的异常提供特定的处理逻辑。当应用程序抛出匹配的异常时,Spring Boot会自动调用相应的异常处理方法。 -
@ResponseStatus
注解:
@ResponseStatus
是一个用于指定响应状态码的注解。通过在异常处理方法上添加@ResponseStatus
注解,你可以指定当特定的异常被捕获时,应返回的HTTP响应状态码。在示例代码中,@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
指定了在处理Exception
异常时返回500状态码,而@ResponseStatus(HttpStatus.BAD_REQUEST)
指定了在处理NullPointerException
异常时返回400状态码。
这些注解是Spring框架提供的一些强大的功能,它们可以帮助你轻松实现统一异常处理和自定义响应。通过使用这些注解,你可以更加灵活地处理不同类型的异常,并返回适当的响应状态码。
三、项目中实现统一异常处理
当在Spring Boot项目中实现统一异常处理时,有几种常见的方式。以下是其中几种方法的简要介绍和示例代码:
1. 使用@ControllerAdvice注解和@ExceptionHandler方法
这是最常见和推荐的方式,通过定义一个全局异常处理器类,使用@ControllerAdvice注解标记,并在其中定义多个@ExceptionHandler方法来处理不同类型的异常。每个@ExceptionHandler方法可以处理特定类型的异常,并返回适当的错误响应。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception ex) {
// 在这里处理异常逻辑
// 例如记录日志、返回特定的错误信息等
String errorMessage = "发生异常: " + ex.getMessage();
return new ResponseEntity<>(errorMessage, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<String> handleNullPointerException(NullPointerException ex) {
// 处理空指针异常的逻辑
String errorMessage = "发生空指针异常: " + ex.getMessage();
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
}
2. 使用@ControllerAdvice注解和@ResponseStatus注解
在这种方法中,我们可以在全局异常处理器类中使用@ExceptionHandler方法来处理异常,并结合@ResponseStatus注解来指定特定的HTTP响应状态码。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleException(Exception ex) {
// 在这里处理异常逻辑
// 例如记录日志等
}
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleNullPointerException(NullPointerException ex) {
// 处理空指针异常的逻辑
}
}
3. 使用自定义的异常类和@ControllerAdvice注解
如果你想要更细粒度地处理不同类型的异常,你可以自定义异常类,并在全局异常处理器中使用@ExceptionHandler方法来处理这些自定义异常。
当实现自定义异常类CustomException
时,你需要创建一个继承自Exception
或其子类的类,并添加自定义的异常信息和构造方法。
下面是一个示例代码,展示了如何实现一个简单的CustomException
类:
public class CustomException extends Exception {
//`CustomException(String message)`接受一个字符串参数`message`,用于设置异常的详细信息。
public CustomException(String message) {
super(message);
}
// 可以添加其他构造方法,根据需要进行重载
}
当你在代码中抛出CustomException
时,可以使用以下方式:
throw new CustomException("自定义异常信息");
通过这种方式,你就可以在你的Spring Boot应用程序中使用自定义异常类CustomException
,并在全局异常处理器中捕获并处理它。
全局异常处理器:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity<String> handleCustomException(CustomException ex) {
// 处理自定义异常的逻辑
String errorMessage = "发生自定义异常: " + ex.getMessage();
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
}
四、实战案例
这是项目中处理全局异常的一个案例代码,帮助我们更好的理解项目中统一处理处理的优点.
这段代码定义了一个全局异常处理器类ExceptionAdvice
,它使用@RestControllerAdvice
注解标记,并包含两个异常处理方法。
@Slf4j // 自动生成名为"log"的日志记录器
@RestControllerAdvice // 标记为全局异常处理器
public class ExceptionAdvice {
@ResponseBody // 将返回值直接转换为响应体
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 设置响应状态码为500
@ExceptionHandler(Exception.class) // 处理Exception及其子类的异常
public String exceptionHandler(Exception e) {
JSONObject json = new JSONObject(); // 创建JSON对象
// 处理后端验证失败产生的异常
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
json.set("error", exception.getBindingResult().getFieldError().getDefaultMessage());
}
// 处理业务异常
else if (e instanceof ProjectSelfException) {
log.error("执行异常", e); // 记录异常日志
ProjectSelfException exception = (ProjectSelfException) e;
json.set("error", exception.getMsg());
}
// 处理其余的异常
else {
log.error("执行异常", e); // 记录异常日志
json.set("error", "执行异常");
}
return json.toString(); // 将JSON对象转换为字符串并返回
}
@ResponseBody // 将返回值直接转换为响应体
@ResponseStatus(HttpStatus.UNAUTHORIZED) // 设置响应状态码为401
@ExceptionHandler(NotLoginException.class) // 处理NotLoginException及其子类的异常
public String unLoginHandler(Exception e) {
JSONObject json = new JSONObject(); // 创建JSON对象
json.set("error", e.getMessage()); // 设置错误信息
return json.toString(); // 将JSON对象转换为字符串并返回
}
}
(1)
@RestControllerAdvice
注解是Spring框架提供的一个组合注解,它是@ControllerAdvice
和@ResponseBody
两个注解的结合。
(2)当你使用@RestControllerAdvice
注解标记一个类时,它将被识别为全局异常处理器,并且处理方法的返回值将直接作为响应体返回。
exceptionHandler
方法使用@ExceptionHandler(Exception.class)
注解来处理Exception
及其子类的异常。在方法内部,它根据不同的异常类型进行处理。如果是MethodArgumentNotValidException
异常,它从异常对象中获取验证失败的详细信息,并将其设置到JSON对象中。如果是ProjectSelfException
异常,它记录异常日志并将异常信息设置到JSON对象中。对于其他类型的异常,它也会记录异常日志并将通用的错误信息设置到JSON对象中。最后,将JSON对象转换为字符串并返回。
@ResponseBody // 将返回值直接转换为响应体
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 设置响应状态码为500
@ExceptionHandler(Exception.class) // 处理Exception及其子类的异常
public String exceptionHandler(Exception e) {
JSONObject json = new JSONObject(); // 创建JSON对象
// 处理后端验证失败产生的异常
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
json.set("error", exception.getBindingResult().getFieldError().getDefaultMessage());
}
// 处理业务异常
else if (e instanceof ProjectSelfException) {
log.error("执行异常", e); // 记录异常日志
ProjectSelfException exception = (ProjectSelfException) e;
json.set("error", exception.getMsg());
}
// 处理其余的异常
else {
log.error("执行异常", e); // 记录异常日志
json.set("error", "执行异常");
}
return json.toString(); // 将JSON对象转换为字符串并返回
}
unLoginHandler
方法使用@ExceptionHandler(NotLoginException.class)
注解来处理NotLoginException
及其子类的异常。在方法内部,它将异常的错误信息设置到JSON对象中,并将JSON对象转换为字符串并返回。
@ResponseBody // 将返回值直接转换为响应体
@ResponseStatus(HttpStatus.UNAUTHORIZED) // 设置响应状态码为401
@ExceptionHandler(NotLoginException.class) // 处理NotLoginException及其子类的异常
public String unLoginHandler(Exception e) {
JSONObject json = new JSONObject(); // 创建JSON对象
json.set("error", e.getMessage()); // 设置错误信息
return json.toString(); // 将JSON对象转换为字符串并返回
}
这些注解和代码实现了统一的异常处理,并将异常信息以JSON格式返回给客户端。同时,使用@Slf4j
注解生成的日志记录器可以方便地记录异常日志。