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
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)
|