Java自定义异常需分层设计(Base/Business/System/External)、构造时携带结构化上下文信息、全局统一拦截返回标准化响应,确保排障高效与前后端协作顺畅。

Java 中自定义异常不是写个 class 就完事,关键在于怎么让它真正参与业务逻辑、统一处理、支撑排障和前后端协作。落地效果好不好,取决于设计是否分层、信息是否结构化、抛出和捕获是否克制。
明确异常分类体系,避免“一把抓”
不要只建一个 MyException。推荐按领域+职责分层,比如:
-
BaseException:抽象父类,继承
RuntimeException,固定携带errorCode(如"USER_NOT_FOUND")、errorMessage(前端展示用)、debugMessage(含参数快照,仅日志记录) - BusinessException:所有业务校验失败、状态非法等场景,如登录失败、库存不足、订单重复提交
- SystemException:技术故障,如数据库超时、Redis连接断开、Feign调用失败
- ExternalServiceException:第三方服务返回错误(如支付网关拒付、短信平台限流),便于单独熔断或降级
构造异常时带上下文,不丢关键现场信息
抛异常不是只写一句“用户名错误”,而是把能帮助定位的变量带上:
- 用构造函数注入原始参数:比如
new BusinessException(ErrorCode.USER_LOGIN_FAIL, "密码错误", Map.of("username", username, "ip", clientIp)) - 保留异常链:当底层抛出
SQLException,上层应包装为SystemException并传入原异常作为 cause,确保堆栈不中断 - 避免在
catch块里吞掉异常或只打印e.getMessage()——它不含堆栈,查不到哪行代码出的问题
全局统一拦截,分离“给用户看”和“给开发看”
用 @ControllerAdvice + @ExceptionHandler 统一收口,做到:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 对
BusinessException返回 HTTP 200 + 标准 JSON:{"code": "USER_NOT_FOUND", "message": "用户不存在,请检查手机号"} - 对
SystemException返回 HTTP 500,并自动记录完整堆栈 +debugMessage到 ELK 或日志系统 - 所有异常响应体结构一致,前端无需判断不同字段名;敏感信息(如 SQL、密钥)绝不进
errorMessage
配合规范使用,不滥用也不遗漏
落地不是靠技术,靠团队共识:
- 约定所有业务校验失败必须抛
BusinessException,禁止用if (...) { return; }静默跳过 - DAO 层出现数据库异常,必须转为
SystemException向上抛,不能让SQLException泄露到 service 或 controller - 新增错误码要走评审,写进接口文档和前端常量类,避免 “1001” 在不同模块含义打架
- 测试用例里覆盖典型异常路径,比如模拟 Redis 不可用时,是否返回预期提示而非 500 页面
不复杂但容易忽略。核心就三点:分得清(分类)、带得全(上下文)、收得住(统一出口)。

















