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.

288 lines
15 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)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向全局异常处理系统,系统性说明 GlobalExceptionHandlerAdvice 如何统一捕获并处理业务异常、参数异常、权限异常与资源不存在异常;详解各自定义异常类的用途与使用场景;阐述异常响应格式、错误码规范与调试信息输出策略;并提供最佳实践与扩展自定义异常类型的方法。目标是帮助开发者快速理解、正确使用并安全扩展该体系。
## 项目结构
全局异常处理相关代码集中在基础模块 crm-base 中:
- 统一异常处理器位于 advice 包
- 自定义异常类位于 domain/exception 包
- 统一响应体与错误码枚举位于 domain/result 包
```mermaid
graph TB
subgraph "基础模块 crm-base"
A["advice/GlobalExceptionHandlerAdvice"] --> B["domain/exception/*"]
A --> C["domain/result/Result"]
A --> D["domain/result/ResultCodeEnum"]
B1["BusinessErrorException"]:::node
B2["MissingParameterException"]:::node
B3["PermissionErrorException"]:::node
B4["ResourceNotExistException"]:::node
B --> B1
B --> B2
B --> B3
B --> B4
end
classDef node fill:#fff,stroke:#333,stroke-width:1px;
```
图表来源
- [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)
章节来源
- [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)
## 核心组件
- 全局异常处理器:集中拦截控制器层抛出的各类异常,转换为统一的响应体。
- 自定义异常族:
- 业务异常:用于表达明确的业务规则不满足或流程不可继续。
- 参数异常:用于校验失败或缺失必填参数等输入问题。
- 权限异常:用于鉴权失败、越权访问等安全相关问题。
- 资源不存在异常:用于查询不到目标实体或资源的情况。
- 统一响应体:对外暴露一致的 JSON 结构,包含状态码、消息、数据与可选的调试信息。
- 错误码枚举:集中管理业务错误码,保证前后端一致性与可维护性。
章节来源
- [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)
## 架构总览
下图展示了请求进入控制器后,若抛出异常,如何通过全局异常处理器统一转换为标准响应体的流程。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Controller as "控制器"
participant Advice as "全局异常处理器"
participant Result as "统一响应体"
participant Enum as "错误码枚举"
Client->>Controller : "发起HTTP请求"
Controller-->>Client : "抛出异常(业务/参数/权限/资源不存在)"
Note over Controller : "异常由框架传播至AOP/拦截层"
Controller->>Advice : "异常被全局处理器捕获"
Advice->>Enum : "根据异常类型选择错误码"
Advice->>Result : "构建统一响应体(含状态码/消息/数据/调试信息)"
Advice-->>Client : "返回标准化JSON响应"
```
图表来源
- [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)
## 详细组件分析
### 全局异常处理器(GlobalExceptionHandlerAdvice)
职责与行为要点:
- 统一入口:通过注解将方法注册为全局异常处理器,拦截指定异常类型。
- 分类处理:针对业务异常、参数异常、权限异常、资源不存在异常分别映射到不同错误码与提示语。
- 响应构造:基于统一响应体封装结果,确保字段稳定、语义清晰。
- 调试信息:在开发环境或特定条件下输出堆栈摘要或追踪ID,便于定位问题。
- 兜底策略:对未预期异常进行安全降级,避免泄露敏感信息。
建议关注点:
- 异常优先级与匹配顺序,避免误捕获。
- 日志级别控制,生产环境避免打印敏感内容。
- 与全局过滤器/拦截器的协作,如链路追踪ID透传。
章节来源
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
### 自定义异常族
#### 业务异常(BusinessErrorException)
- 用途:表达业务规则不满足、流程不可继续等“有明确业务含义”的错误。
- 典型场景:库存不足、订单状态不允许操作、审批流不通过等。
- 设计要点:携带业务错误码与可读消息,便于前端展示与后端统计。
章节来源
- [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java)
#### 参数异常(MissingParameterException)
- 用途:参数缺失、格式错误或校验失败时抛出。
- 典型场景:必填字段为空、日期格式不正确、分页参数非法等。
- 设计要点:提供清晰的字段级或整体提示信息,利于前端表单反馈。
章节来源
- [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java)
#### 权限异常(PermissionErrorException)
- 用途:鉴权失败、角色/权限不足、越权访问等安全问题。
- 典型场景:无访问菜单权限、跨部门数据访问受限、Token无效等。
- 设计要点:避免泄露具体权限细节,仅给出通用提示。
章节来源
- [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java)
#### 资源不存在异常(ResourceNotExistException)
- 用途:查询不到目标实体或资源。
- 典型场景:用户ID不存在、附件已删除、配置项缺失等。
- 设计要点:区分“逻辑不存在”和“系统错误”,避免误报。
章节来源
- [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java)
### 统一响应体与错误码
#### 统一响应体(Result)
- 字段约定:状态码、消息、数据、可选调试信息(如traceId)。
- 序列化:保持稳定的JSON键名与类型,便于前端解析。
- 扩展性:预留扩展字段空间,支持后续增强(如请求ID、时间戳等)。
章节来源
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
#### 错误码枚举(ResultCodeEnum)
- 作用:集中定义错误码与默认消息,保证一致性。
- 规范:按模块/层级划分,避免冲突;提供友好提示与内部编码分离。
- 使用:在异常构造或处理器中引用,减少硬编码。
章节来源
- [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java)
### 异常处理流程图
下图概括了从异常抛出到统一响应的关键分支逻辑。
```mermaid
flowchart TD
Start(["进入全局异常处理器"]) --> Type{"异常类型判断"}
Type --> |业务异常| Biz["映射业务错误码<br/>组装业务消息"]
Type --> |参数异常| Param["映射参数错误码<br/>组装参数提示"]
Type --> |权限异常| Perm["映射权限错误码<br/>组装鉴权提示"]
Type --> |资源不存在| Res["映射资源错误码<br/>组装资源提示"]
Type --> |其他异常| Fallback["兜底处理<br/>安全降级+脱敏"]
Biz --> Build["构建统一响应体"]
Param --> Build
Perm --> Build
Res --> Build
Fallback --> Build
Build --> Return(["返回标准化JSON"])
```
图表来源
- [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)
## 依赖关系分析
- 全局异常处理器依赖统一响应体与错误码枚举,实现解耦与复用。
- 自定义异常之间相互独立,便于按需组合与扩展。
- 与上层控制器松耦合:控制器只需抛出异常,无需关心响应构造。
```mermaid
classDiagram
class GlobalExceptionHandlerAdvice {
+处理业务异常()
+处理参数异常()
+处理权限异常()
+处理资源不存在异常()
+兜底处理()
}
class BusinessErrorException
class MissingParameterException
class PermissionErrorException
class ResourceNotExistException
class Result
class ResultCodeEnum
GlobalExceptionHandlerAdvice --> Result : "构造响应"
GlobalExceptionHandlerAdvice --> ResultCodeEnum : "获取错误码"
GlobalExceptionHandlerAdvice --> BusinessErrorException : "捕获"
GlobalExceptionHandlerAdvice --> MissingParameterException : "捕获"
GlobalExceptionHandlerAdvice --> PermissionErrorException : "捕获"
GlobalExceptionHandlerAdvice --> ResourceNotExistException : "捕获"
```
图表来源
- [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)
章节来源
- [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)
## 性能考虑
- 异常路径属于非主流程,应尽量减少异常抛出的频率,优先使用条件判断与校验。
- 日志输出需分级控制,生产环境避免打印完整堆栈与敏感信息。
- 统一响应体构造应避免不必要的对象创建与复杂计算。
- 错误码枚举查找应为常量级开销,避免动态解析。
## 故障排查指南
常见问题与定位思路:
- 未捕获异常:检查是否遗漏对应异常类型的处理器分支。
- 错误码不一致:核对枚举定义与实际使用是否同步更新。
- 调试信息泄露:确认仅在受控环境下输出,生产环境脱敏。
- 前端解析失败:检查统一响应体字段命名与类型是否稳定。
章节来源
- [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)
## 结论
通过全局异常处理器与自定义异常族的协同,系统实现了异常处理的标准化、可观测与可扩展。遵循本文的最佳实践,可在保障用户体验的同时提升系统的可维护性与稳定性。
## 附录
### 最佳实践
- 在业务层尽早校验与断言,减少异常发生概率。
- 使用统一响应体与错误码,避免散落的字符串与魔法值。
- 对敏感信息进行脱敏,避免堆栈与配置泄露。
- 为每个异常类型提供清晰的错误码与提示语,便于前端展示与监控告警。
### 自定义异常扩展方法
- 新增异常类型:在 domain/exception 下新建异常类,继承基础异常或参照现有模式。
- 注册处理器:在全局异常处理器中添加对应分支,映射错误码与消息。
- 补充错误码:在错误码枚举中新增条目,保持命名与分组规范。
- 单元测试:覆盖正常路径与异常路径,确保响应体结构与字段稳定。
章节来源
- [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)
- [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)