You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
18 KiB

1 month ago
# 异常处理机制
<cite>
**本文引用的文件**
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件系统化梳理 CRM 后端项目的异常处理机制,重点围绕全局异常处理器 GlobalExceptionHandlerAdvice、自定义业务异常类型(BusinessErrorException、MissingParameterException、PermissionErrorException、ResourceNotExistException)以及统一响应格式(Result、ResultCodeEnum)进行说明。文档涵盖异常分类、错误码规范、统一响应格式、最佳实践与调试技巧,帮助开发者快速定位问题并规范地抛出与处理异常。
## 项目结构
异常处理相关代码集中在基础模块 crm-base 中:
- 全局异常处理器位于 advice 包
- 自定义异常位于 domain/exception 包
- 统一响应体与错误码枚举位于 domain/result 包
- 断言工具位于 utils 包,常用于前置参数校验与快速抛出自定义异常
```mermaid
graph TB
subgraph "基础模块 crm-base"
A["advice<br/>GlobalExceptionHandlerAdvice"]
B["domain/exception<br/>BusinessErrorException<br/>MissingParameterException<br/>PermissionErrorException<br/>ResourceNotExistException"]
C["domain/result<br/>Result<br/>ResultCodeEnum"]
D["utils<br/>AssertUtils"]
end
A --> C
A --> B
D --> B
```
图表来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
## 核心组件
- 全局异常处理器 GlobalExceptionHandlerAdvice
- 职责:集中捕获控制器层抛出的各类异常,将其转换为统一的 Result 响应体,确保前端/调用方获得一致的错误结构。
- 关键点:按异常类型分别映射到不同的 ResultCodeEnum;对未知异常提供兜底处理;避免泄露敏感信息。
- 自定义异常类型
- BusinessErrorException:通用业务异常,携带业务错误码与消息,用于业务规则不满足或流程失败等场景。
- MissingParameterException:参数缺失或非法异常,用于入参校验失败场景。
- PermissionErrorException:权限不足异常,用于鉴权未通过场景。
- ResourceNotExistException:资源不存在异常,用于查询不到目标数据场景。
- 统一响应体 Result
- 职责:对外暴露的标准化返回结构,包含状态码、消息、数据等字段。
- 错误码枚举 ResultCodeEnum
- 职责:统一定义业务错误码与描述,保证错误码唯一性与可读性。
- 断言工具 AssertUtils
- 职责:提供便捷的前置条件检查方法,条件不满足时直接抛出对应的自定义异常,简化业务代码中的校验逻辑。
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
## 架构总览
下图展示了请求进入控制器后,异常如何被全局处理器捕获并转换为统一响应的整体流程。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Controller as "控制器"
participant Service as "业务服务"
participant Utils as "断言工具"
participant Handler as "全局异常处理器"
participant Resp as "统一响应体"
Client->>Controller : "发起HTTP请求"
Controller->>Service : "执行业务逻辑"
Service->>Utils : "参数校验/业务断言"
Utils-->>Service : "条件不满足则抛出异常"
Service-->>Controller : "正常返回或抛出异常"
Controller-->>Handler : "异常向上抛出"
Handler->>Handler : "根据异常类型选择错误码"
Handler->>Resp : "封装为统一响应体"
Handler-->>Client : "返回统一响应"
```
图表来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
## 详细组件分析
### 全局异常处理器 GlobalExceptionHandlerAdvice
- 设计要点
- 使用 Spring 的全局异常处理注解,拦截控制器层抛出的异常。
- 针对不同类型的异常分别映射到 ResultCodeEnum 中的对应错误码。
- 将异常信息封装进 Result,确保返回结构一致。
- 对未知异常进行兜底处理,避免泄露堆栈信息。
- 典型处理路径
- 业务异常 -> 业务错误码 -> 统一响应
- 参数异常 -> 参数错误码 -> 统一响应
- 权限异常 -> 权限错误码 -> 统一响应
- 资源不存在 -> 资源错误码 -> 统一响应
- 其他异常 -> 系统错误码 -> 统一响应
- 注意事项
- 不要记录敏感信息到日志或响应体。
- 保持错误码与消息的一致性,便于前端提示与监控告警。
- 对于可恢复的业务错误,优先使用业务异常而非系统异常。
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
### 自定义异常类型
- BusinessErrorException
- 用途:表达业务规则不满足、流程失败等场景。
- 特点:携带业务错误码与消息,便于上层统一处理与展示。
- MissingParameterException
- 用途:入参缺失或非法,通常由参数校验失败触发。
- 特点:明确指向“参数”问题,便于前端提示用户补充或修正。
- PermissionErrorException
- 用途:鉴权未通过,如角色不足、权限不足。
- 特点:与认证/授权体系配合,统一返回权限错误码。
- ResourceNotExistException
- 用途:查询不到目标资源,如 ID 不存在、数据已被删除。
- 特点:明确区分“业务失败”和“资源不存在”,利于前端差异化提示。
章节来源
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
### 统一响应格式与错误码规范
- Result
- 作用:对外统一返回结构,包含状态码、消息、数据等字段。
- 建议:成功与失败均使用该结构,便于前端统一解析。
- ResultCodeEnum
- 作用:统一定义错误码与描述,保证错误码唯一性与可读性。
- 建议:按领域划分错误码段,避免冲突;新增错误码需同步更新文档与测试。
章节来源
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
### 断言工具 AssertUtils 的使用
- 作用:在业务方法开头进行前置条件校验,条件不满足时直接抛出对应的自定义异常。
- 优势:减少样板代码,提升可读性与一致性。
- 示例场景
- 校验必填参数是否为空
- 校验业务状态是否允许当前操作
- 校验权限或资源是否存在
章节来源
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
### 异常处理流程图
下图展示从参数校验到统一响应生成的关键步骤。
```mermaid
flowchart TD
Start(["入口"]) --> CheckParam["参数校验"]
CheckParam --> ParamOK{"参数有效?"}
ParamOK --> |否| ThrowParamEx["抛出参数异常"]
ParamOK --> |是| BizCheck["业务断言"]
BizCheck --> BizOK{"业务合法?"}
BizOK --> |否| ThrowBizEx["抛出业务异常"]
BizOK --> |是| Process["执行业务逻辑"]
Process --> Success["返回成功结果"]
ThrowParamEx --> Handle["全局异常处理器捕获"]
ThrowBizEx --> Handle
Handle --> MapCode["映射错误码"]
MapCode --> WrapResp["封装统一响应"]
WrapResp --> End(["结束"])
Success --> End
```
图表来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
## 依赖关系分析
- 全局异常处理器依赖统一响应体与错误码枚举,负责将异常转换为标准响应。
- 业务层通过断言工具抛出自定义异常,降低重复校验代码。
- 各自定义异常之间相互独立,仅通过全局处理器聚合处理。
```mermaid
classDiagram
class GlobalExceptionHandlerAdvice {
+处理异常()
}
class Result {
+状态码
+消息
+数据
}
class ResultCodeEnum {
+错误码
+描述
}
class BusinessErrorException
class MissingParameterException
class PermissionErrorException
class ResourceNotExistException
class AssertUtils {
+断言方法()
}
GlobalExceptionHandlerAdvice --> Result : "封装响应"
GlobalExceptionHandlerAdvice --> ResultCodeEnum : "映射错误码"
AssertUtils --> BusinessErrorException : "抛出"
AssertUtils --> MissingParameterException : "抛出"
AssertUtils --> PermissionErrorException : "抛出"
AssertUtils --> ResourceNotExistException : "抛出"
```
图表来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
## 性能考虑
- 异常路径应避免频繁创建大对象,尽量复用错误码与消息。
- 全局异常处理器中尽量减少复杂计算与外部IO,确保快速返回。
- 参数校验尽量前置,减少无效业务执行带来的开销。
- 日志记录遵循最小化原则,避免打印敏感信息与超大堆栈。
## 故障排查指南
- 常见问题
- 前端收到非预期状态码:检查 ResultCodeEnum 是否正确映射。
- 错误消息过于笼统:细化异常消息,结合上下文信息。
- 敏感信息泄露:审查日志与响应体,移除敏感字段。
- 调试技巧
- 在业务方法入口处添加断言,快速定位参数问题。
- 使用全局异常处理器日志输出异常类型与错误码,便于追踪。
- 对关键分支编写单元测试,覆盖异常路径。
- 排查步骤
- 确认请求参数是否符合接口约定。
- 检查业务断言与权限校验逻辑。
- 查看全局异常处理器日志与错误码映射。
- 复现问题并缩小范围,逐步定位根因。
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- [AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)
## 结论
本项目通过全局异常处理器与自定义异常类型构建了清晰、一致的异常处理机制。借助统一响应体与错误码枚举,前后端交互更加稳定可靠。建议在业务开发中优先使用断言工具进行前置校验,并在必要时抛出明确的自定义异常,以提升可维护性与可观测性。
## 附录
- 最佳实践
- 始终使用统一响应体,避免混用多种返回结构。
- 错误码按领域划分,命名清晰且唯一。
- 异常消息面向用户友好,技术细节放在日志中。
- 对关键异常路径编写单测,保障回归质量。
- 参考路径
- 全局异常处理器:[GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
- 统一响应体:[Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
- 错误码枚举:[ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
- 断言工具:[AssertUtils.java](file://crm-base/src/main/java/com/crm/base/utils/AssertUtils.java)