在Spring Boot中,优雅地处理全局异常可以通过定义一个全局异常处理器来实现。全局异常处理器可以帮助你集中处理应用程序中的所有异常,并返回统一的响应格式。以下是如何实现全局异常处理器的步骤:
-
创建自定义异常类(可选):如果你有特定的业务异常,可以创建自定义异常类。
-
创建全局异常处理器:使用
@ControllerAdvice注解来定义一个全局异常处理器类。 -
定义异常处理方法:在全局异常处理器类中,使用
@ExceptionHandler注解来定义处理特定异常的方法。
下面是一个简单的示例:
自定义异常类(可选)
public class CustomException extends RuntimeException {
private int errorCode;
public CustomException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
全局异常处理器
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义异常
@ExceptionHandler(CustomException.class)
public ResponseEntity<?> handleCustomException(CustomException ex, WebRequest request) {
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage(), ex.getErrorCode());
return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST);
}
// 处理其他异常
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleGlobalException(Exception ex, WebRequest request) {
ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), 500);
return new ResponseEntity<>(apiError, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
class ApiError {
private HttpStatus status;
private String message;
private int errorCode;
public ApiError(HttpStatus status, String message, int errorCode) {
this.status = status;
this.message = message;
this.errorCode = errorCode;
}
// Getters and setters
public HttpStatus getStatus() {
return status;
}
public void setStatus(HttpStatus status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
使用自定义异常
在你的服务或控制器中,你可以抛出自定义异常:
@Service
public class SomeService {
public void someMethod() {
// 某些业务逻辑
if (someCondition) {
throw new CustomException("Something went wrong", 400);
}
}
}
通过这种方式,你可以集中管理所有的异常处理逻辑,并且可以确保返回给客户端的错误信息格式一致。
在 Spring Boot 应用程序中,全局异常处理是非常重要的一个方面。它可以帮助你集中管理和统一处理应用程序中的各种异常,从而提高代码的可维护性和用户体验。以下是一些关于 Spring Boot 全局异常处理的技巧和知识:
1. 使用 @ControllerAdvice 注解
@ControllerAdvice 注解用于定义全局异常处理器类。它可以捕获所有控制器中的异常,并提供统一的错误响应。
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), "Resource Not Found", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 可以添加更多的 @ExceptionHandler 方法来处理特定的异常
}
2. 自定义异常类
为了更好地管理不同的异常情况,可以创建自定义异常类。这些异常类可以继承自 RuntimeException 或其他异常类。
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
public class InvalidRequestException extends RuntimeException {
public InvalidRequestException(String message) {
super(message);
}
}
3. 统一的错误响应格式
定义一个统一的错误响应格式,以便在发生异常时返回一致的错误信息。
public class ErrorResponse {
private int status;
private String message;
private String details;
public ErrorResponse(int status, String message, String details) {
this.status = status;
this.message = message;
this.details = details;
}
// Getters and Setters
}
4. 使用 @ResponseStatus 注解
可以在自定义异常类上使用 @ResponseStatus 注解,指定异常对应的 HTTP 状态码。
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
5. 日志记录
在全局异常处理器中记录异常信息,有助于后续的问题排查和调试。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
logger.error("Internal Server Error: {}", ex.getMessage(), ex);
ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
logger.warn("Resource Not Found: {}", ex.getMessage());
ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), "Resource Not Found", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
6. 响应体的自定义
可以根据不同的客户端需求,自定义响应体的格式。例如,对于 API 客户端,可以返回 JSON 格式的错误信息;对于 Web 客户端,可以返回 HTML 页面。
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView handleResourceNotFoundException(ResourceNotFoundException ex) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("message", "Resource Not Found");
modelAndView.addObject("details", ex.getMessage());
return modelAndView;
}
}
7. 异常处理的优先级
Spring Boot 会按照方法签名的特异性来决定哪个 @ExceptionHandler 方法应该被调用。因此,更具体的异常处理方法会优先于更通用的异常处理方法。
8. 使用 AOP 切面
除了 @ControllerAdvice,还可以使用 AOP 切面来实现全局异常处理。这种方式更加灵活,可以应用于更广泛的场景。
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class GlobalExceptionAspect {
@AfterThrowing(pointcut = "execution(* com.ctsi..*(..))", throwing = "ex")
public void handleException(Exception ex) {
// 处理异常
}
}
9. 集成第三方库
可以集成第三方库,如 Hystrix、Resilience4j 等,来增强异常处理的能力,实现熔断、重试等功能。
10. 测试全局异常处理
使用 JUnit 和 Mockito 等测试框架,编写单元测试来验证全局异常处理的正确性。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.doThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest
public class GlobalExceptionHandlerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SomeService someService;
@Test
public void testHandleException() throws Exception {
doThrow(new RuntimeException("Test exception")).when(someService).someMethod();
mockMvc.perform(get("/some-endpoint"))
.andExpect(status().isInternalServerError());
}
}
通过以上技巧和知识,你可以有效地管理和处理 Spring Boot 应用程序中的各种异常,提升应用的健壮性和用户体验。