SpringBoot 异常处理的最佳实践
在 Web 开发中,异常处理是非常重要的一环。在 SpringBoot 框架中,异常处理方式有很多种,但是如何选择最佳实践呢?本文将介绍 SpringBoot 异常处理的最佳实践,并附带代码示例。
异常处理的最佳实践
异常处理的最佳实践包括以下几个方面:
- 统一异常处理
在 SpringBoot 中,我们可以使用 @ControllerAdvice 注解实现全局异常处理。使用 @ControllerAdvice 注解,我们可以集中处理所有 Controller 层抛出的异常,而不必在每个 Controller 中单独处理。在 GlobalExceptionHandler 类中定义异常处理方法,并根据具体的业务需求对异常进行相应的处理,可以大大提高代码的可维护性和可读性。
- 自定义异常
在 SpringBoot 中,我们可以通过自定义异常类来处理特定的异常情况,并在 GlobalExceptionHandler 类中定义对应的异常处理方法。自定义异常类可以继承 RuntimeException 或 Exception 类,根据具体的业务需求进行选择。自定义异常类可以包含异常码、异常信息等属性,方便我们对异常进行更加细致的处理。
- 统一返回结果
在 SpringBoot 中,我们可以定义一个统一的返回结果类,用于封装接口返回结果。在接口返回时,我们可以使用该类作为返回值,并根据具体的业务需求封装返回结果的状态码、消息和数据等信息。
- 参数校验
在 SpringBoot 中,我们可以使用 javax.validation.constraints 包中的注解对参数进行校验。在 Controller 层中,我们可以使用 @Validated 注解对参数进行校验,并在对应的 Service 层方法中进行处理。如果参数校验失败,会抛出 MethodArgumentNotValidException 异常,在 GlobalExceptionHandler 类中的 handleMethodArgumentNotValidException 方法中进行处理。
- 日志记录
在 SpringBoot 中,我们可以使用日志框架记录系统运行时的异常信息。在 GlobalExceptionHandler 类中的异常处理方法中,我们可以使用日志框架记录异常发生的时间、异常类型、异常信息等相关信息,方便我们进行排查和问题定位。
完整代码示例
下面是一个完整的使用 SpringBoot 异常处理的示例代码:
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorResponse handleException(Exception e) {
logger.error("系统异常", e);
return new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统异常");
}
@ExceptionHandler(UserNotFoundException.class)
@ResponseBody
public ErrorResponse handleUserNotFoundException(UserNotFoundException e) {
logger.warn("用户不存在", e);
return new ErrorResponse(HttpStatus.NOT_FOUND.value(), "用户不存在");
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
List<String> errorMessages = new ArrayList<>();
for (ObjectError error : allErrors) {
errorMessages.add(error.getDefaultMessage());
}
String errorMessage = String.join(",", errorMessages);
logger.warn("参数无效", e);
return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), errorMessage);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/add")
public Result addUser(@Validated @RequestBody User user) {
userService.addUser(user);
return Result.success();
}
@GetMapping("/{id}")
public Result getUserById(@PathVariable("id") Long id) {
User user = userService.getUserById(id);
if (user == null) {
throw new UserNotFoundException();
}
return Result.success(user);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void addUser(User user) {
userDao.save(user);
}
public User getUserById(Long id) {
return userDao.findById(id).orElse(null);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@NotNull(message = "用户名不能为空")
privateLong id;
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success() {
return new Result<>(HttpStatus.OK.value(), "成功", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(HttpStatus.OK.value(), "成功", data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {
private Integer code;
private String message;
}
public class UserNotFoundException extends RuntimeException {
}
@Repository
public interface UserDao extends JpaRepository<User, Long> {
}
在上面的代码示例中,我们定义了一个 GlobalExceptionHandler 类,用于处理全局异常。在该类中,我们定义了三个异常处理方法:handleException、handleUserNotFoundException 和 handleMethodArgumentNotValidException。分别用于处理系统异常、用户不存在异常和参数无效异常。在每个方法中,我们都使用日志框架记录异常信息,并返回封装好的 ErrorResponse 对象。
在 UserController 类中,我们使用 @Validated 注解对 addUser 方法中的 User 参数进行了校验。如果校验失败,会抛出 MethodArgumentNotValidException 异常,在 GlobalExceptionHandler 类中的 handleMethodArgumentNotValidException 方法中进行处理。
在 UserService 类中,我们使用了 JpaRepository 接口,对 User 数据进行了持久化操作。
最后,我们定义了 User、Result、ErrorResponse 和 UserNotFoundException 四个类,分别用于封装用户信息、接口返回结果、错误信息和用户不存在异常。
结论
在本文中,我们介绍了 SpringBoot 异常处理的最佳实践,并附带了完整的代码示例。在使用 SpringBoot 进行 Web 开发时,我们应该充分考虑异常处理的方方面面,以保证代码的可维护性和可读性。通过使用统一异常处理、自定义异常、统一返回结果、参数校验和日志记录等技术手段,我们可以更加高效地进行异常处理,提高系统的稳定性和可靠性。