本文的核心重点就是讲解SpringBoot3分层架构中全局异常处理类的设计。
上文中我们已经实现了SpringBoot项目的分层架构
Spring Boot3项目的常见通用整体架构
架构设计需要考虑诸多因素,合理的架构设计可以帮助项目的可维护性、扩展性和可测试性。以下是一些常见的最佳实践和设计建议,适合中大型项目或者复杂的 Spring Boot 3 项目
典型的分层架构可以帮助将关注点分离,推荐使用三层或四层架构:
随着项目规模扩大,考虑将项目拆分为多个模块。这可以通过 Maven/Gradle 的多模块(multi-module)项目来实现:
这样可以确保不同的模块具有明确的职责,项目更加可扩展。
以下是一个推荐的 Spring Boot 项目结构示例:
src
├── main
│ ├── java
│ │ └── com.example.project
│ │ ├── controller // Controller 层,处理请求
│ │ ├── service // Service 层,业务逻辑
│ │ ├── repository // Repository 层,数据持久化
│ │ ├── dto // DTO 类,数据传输对象
│ │ ├── entity // 实体类,映射数据库
│ │ ├── exception // 自定义异常处理
│ │ ├── utils // 通用工具类
│ │ ├── filter // 过滤类
│ │ └── config // 配置类(如 Security, DataSource 等)
│ └── resources
│ ├── application.properties // 应用程序配置文件
│ └── static // 静态资源(前端文件)
└── test // 单元测试和集成测试
UserRepository
接口,然后在 Service 层中依赖接口而不是具体实现。@ControllerAdvice
和 @ExceptionHandler
实现全局异常处理,确保统一的错误响应格式本文的核心重点就是讲解全局异常处理类的设计。 在 Spring Boot 项目中,通常会在项目中封装异常处理类。以下是自定义异常、全局异常处理器和统一的错误响应格式。
定义一个统一的 ErrorResponse
类,用于封装异常处理的响应数据。
package com.javastudy.springboot3framework.myapp.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @author zhizhou 2024/9/10 13:28
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ErrorResponse {
private LocalDateTime timestamp; //时间戳
private String message;//消息
private String details; //详细信息
private int status;//状态
private int code; //错误码
public ErrorResponse(String message, String details, int status, int code) {
this.timestamp = LocalDateTime.now();
this.message = message;
this.details = details;
this.status = status;
this.code = code;
}
}
使用 @ControllerAdvice
和 @ExceptionHandler
注解来实现全局异常处理器,统一处理应用程序中的异常。
package com.javastudy.springboot3framework.myapp.exception;
import lombok.extern.slf4j.Slf4j;
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;
/**
* @author zhizhou 2024/9/10 23:28
*/
@Slf4j
@ControllerAdvice
public class GlobalException3Handler {
// 处理资源未找到异常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
ex.getMessage(),
request.getDescription(false),
HttpStatus.NOT_FOUND.value(),
ErrorCode.RESOURCE_NOT_FOUND.getCode()
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
// 处理无效请求参数异常
@ExceptionHandler(InvalidRequestException.class)
public ResponseEntity<ErrorResponse> handleInvalidRequestException(InvalidRequestException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
ex.getMessage(),
request.getDescription(false),
HttpStatus.BAD_REQUEST.value(),
ErrorCode.INVALID_INPUT.getCode()
);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
// 处理操作失败异常
@ExceptionHandler(OperationFailedException.class)
public ResponseEntity<ErrorResponse> handleOperationFailedException(OperationFailedException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
ex.getMessage(),
request.getDescription(false),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ErrorCode.OPERATION_FAILED.getCode()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 处理所有其他异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
"Internal Server Error",
request.getDescription(false),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ErrorCode.INTERNAL_SERVER_ERROR.getCode()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
//异常-未寻址到资源
public static class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
//异常-请求参数无效
public static class InvalidRequestException extends RuntimeException {
public InvalidRequestException(String message) {
super(message);
}
}
//异常-操作失败
public static class OperationFailedException extends RuntimeException {
public OperationFailedException(String message) {
super(message);
}
}
}
可以通过枚举来管理错误码和消息:
public enum ErrorCode {
RESOURCE_NOT_FOUND(40401, "资源不存在"),
INVALID_INPUT(40001, "入参不合法"),
OPERATION_FAILED(50001, "操作失败"),
INTERNAL_SERVER_ERROR(50000, "内部服务错误");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
到此为止,异常类中 完成了 自定义异常类、全局异常处理器、统一错误响应类、错误码管理等通用的异常处理机制。以上方案经过时间,方案相对通用可复制到自己的项目中。
OK,今天就到这~ 关注我,一起为程序员职业生涯储能续航。