恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

全局异常处理-把错误拦在业务之外

  • 首页
  • 资讯中心
  • /
  • 全局异常处理-把错误拦在业务之外

相关资讯

考试信息一站式查询:从报名时间到备考资料的全流程指南 2026/10/15 0:54:58
“哈哈哈哈哈”为何能成为国民级网络热词?拆解情绪表达与内容运营密码 2026/10/15 0:54:58
C#调用MATLAB绘图实战:COM自动化、句柄管理与嵌入式集成 2026/10/15 0:49:58

最新资讯

H2O-3 机器学习平台核心术语全解:从数据列类型到模型训练与部署的权威指南
browser-logos 归档包 CHANGELOG 深度解读:@browser-logos/chrome-canary_19-48 的版本演进与发布自动化
ShopXO 中的 PSR-3 日志标准:psr/log 接口包的原理与实践指南
Claude Code 桌面版系统提示词:一份 7000 行 Agent「操作手册」是怎么设计的
openvscode-server 内置 npm 扩展全解:脚本任务自动检测、运行与调试指南
visual studio配置libtorch

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

全局异常处理-把错误拦在业务之外

发布时间:2026/10/15 0:54:58
全局异常处理-把错误拦在业务之外 07-全局异常处理-把错误拦在业务之外大家好我是黒漂技术佬。上篇我们把分页壳子PageResult讲透了这篇聊一个平时看不见、出事要你命的东西——全局异常处理。新手写业务最容易踩的坑在一个方法里try-catch包半屏catch 里e.printStackTrace()然后return null。前端拿到null一脸问号不知道是参数错了、没登录还是服务器炸了。更惨的是数据库抛个SQLException直接把堆栈塞进 HTTP 响应用户看见一坨红色异常页面。“URM Ultra的后端用 Spring Boot 3 的RestControllerAdvice把异常收口得极其干净。今天我们就拆开看它是怎么做到的以及为什么业务代码里不该出现 try-catch”。一、先搞懂异常到底该在哪处理很多教程一上来就贴RestControllerAdvice但没讲清楚为什么。我们先建立一条原则业务方法的职责是把事做成不是把错兜住。什么意思假设你写扣库存// 反面教材在业务里疯狂 try-catchpublicvoiddeductStock(LongdeviceId,StringslotNo,intqty){try{DeviceProductdprepository.findByDeviceIdAndSlotNo(deviceId,slotNo);if(dpnull){thrownewRuntimeException(货道不存在);}if(dp.getStock()qty){thrownewRuntimeException(库存不足);}dp.setStock(dp.getStock()-qty);repository.save(dp);}catch(Exceptione){e.printStackTrace();// 错日志没、异常吞了throwe;// 或者 return; 错上加错}}这种写法的毛病业务代码里夹着一堆异常处理读起来像在跟异常搏斗看不清主干逻辑catch后e.printStackTrace()既不进日志框架、也进不了监控系统抛出的还是RuntimeException前端收到一个 500 堆栈根本不知道库存不足是业务逻辑错还是系统崩了。正确思路业务里只管抛统一在一个地方接。抛异常是廉价的、语义明确的怎么把异常翻译成前端能懂的报文交给全局处理器。二、业务异常 BusinessException给错误一个业务身份项目先定义了一个专属异常把业务错误和普通系统错误区分开// 项目源码com.urm.ultra.common.BusinessExceptionGetterpublicclassBusinessExceptionextendsRuntimeException{privatefinalintcode;publicBusinessException(Stringmessage){this(500,message);}publicBusinessException(intcode,Stringmessage){super(message);this.codecode;}}注意三点它继承RuntimeException是运行时异常不用方法签名上写throws业务里随手throw new BusinessException(...)即可带了一个code字段。这就是业务码——Result里那个code0 表示成功非 0 表示各类错误提供了两个构造只传message时默认code500传(code, message)时自定义业务码比如库存不足可以抛new BusinessException(4001, 库存不足)前端就能按码做特定提示。有了它扣库存就能写得极其干净// 项目源码风格示意业务里只抛不抓publicvoiddeductStock(LongdeviceId,StringslotNo,intqty){DeviceProductdprepository.findByDeviceIdAndSlotNo(deviceId,slotNo);if(dpnull){thrownewBusinessException(货道不存在);}if(dp.getStock()null||dp.getStock()qty){thrownewBusinessException(4001,库存不足);}dp.setStock(dp.getStock()-qty);repository.save(dp);}主干逻辑三行错误用throw一句带过。这就是把错误拦在业务之外的第一层含义业务代码只描述正常流程 在异常点抛出自带语义的异常。三、RestControllerAdvice全局的异常接盘侠关键角色登场。RestControllerAdvice是 Spring 的统一异常拦截器专盯所有RestController里抛出的异常。项目里就这么一个类// 项目源码com.urm.ultra.common.GlobalExceptionHandlerSlf4jRestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(BusinessException.class)publicResultVoidhandleBusiness(BusinessExceptione){returnResult.error(e.getCode(),e.getMessage());}ExceptionHandler({MethodArgumentNotValidException.class,BindException.class})publicResultVoidhandleValidation(Exceptione){Stringmsg参数校验失败;if(einstanceofMethodArgumentNotValidExceptionexex.getBindingResult().getFieldError()!null){msgex.getBindingResult().getFieldError().getDefaultMessage();}elseif(einstanceofBindExceptionexex.getBindingResult().getFieldError()!null){msgex.getBindingResult().getFieldError().getDefaultMessage();}returnResult.error(400,msg);}ExceptionHandler(Exception.class)publicResultVoidhandleException(Exceptione){log.error(系统异常,e);returnResult.error(500,系统异常: e.getMessage());}}逐行拆RestControllerAdvice告诉 Spring我是全局异常处理中心作用范围覆盖所有 REST 接口和ControllerAdvice的区别是它默认把返回值当ResponseBody处理直接写 JSON不用再标ResponseBody。Slf4jLombok 注解自动生成log对象兜底异常时打日志用。四、三个 ExceptionHandler 的分工ExceptionHandler(Xxx.class)表示这个方法专门处理 Xxx 类型的异常。匹配顺序是最具体的优先所以三个 handler 的层级是这样1. handleBusiness —— 业务异常原样回传ExceptionHandler(BusinessException.class)publicResultVoidhandleBusiness(BusinessExceptione){returnResult.error(e.getCode(),e.getMessage());}业务里抛的BusinessException这里把它翻译成Result.error(code, message)。比如前面库存不足抛的是new BusinessException(4001, 库存不足)前端就收到{code:4001,message:库存不足,data:null}前端按code4001弹该货道库存不足请重试体验直接拉满。2. handleValidation —— 参数校验失败统一 400ExceptionHandler({MethodArgumentNotValidException.class,BindException.class})publicResultVoidhandleValidation(Exceptione){...}这两个异常来自参数校验下一篇细讲DTO 上标了NotBlank但前端没传Spring 在校验阶段就抛MethodArgumentNotValidException。这里统一转成Result.error(400, 字段消息)——把哪个字段错、错在哪的提示直接塞进message前端照着弹就行。BindException是表单/路径变量绑定失败时抛的同类异常一并接住保证 GET 和 POST 两种校验失败都走同一套返回。3. handleException —— 兜底任何漏网的都归 500ExceptionHandler(Exception.class)publicResultVoidhandleException(Exceptione){log.error(系统异常,e);returnResult.error(500,系统异常: e.getMessage());}这是最后一道防线。任何你没预料的异常——NPE、SQL 报错、JSON 解析失败——都会被它接住转成code500的标准报文同时用log.error把完整堆栈打进日志。这一层至关重要对前端永远拿到结构统一的Result不会出现有时返回 Result、有时抛出 Tomcat 默认错误页的分裂对运维异常进日志了出问题能查对用户不会看到一坨英文堆栈而是一句系统异常: xxx。注意顺序——ExceptionHandler(Exception.class)放在最后。因为它最泛能接所有异常前面的BusinessException、MethodArgumentNotValidException更具体Spring 会优先匹配具体的。如果把Exception放第一业务异常反而会被它吃掉那就乱套了。五、为什么这叫与业务解耦回看开头那句原则现在我们能说清解耦在哪业务方法零 try-catch所有异常沿调用栈一路抛到GlobalExceptionHandler业务代码只剩 happy path 语义化 throw可读性和可维护性暴涨异常翻译集中所有异常 → HTTP 响应的映射只有一个类负责。哪天你想把 500 报文从系统异常: xxx改成服务器开小差了只改这一处响应结构恒定无论成功失败前端永远解析同一套Result{code,message,data}不用为不同错误写不同解析分支日志与对外信息分离兜底异常把堆栈记进日志给开发看对外只回系统异常加简短信息给用户看避免把内部细节泄漏出去。这四点合起来就是把错误拦在业务之外——错误在边界全局处理器被统一栏截、翻译、记录业务核心保持纯粹。六、一个常见误区401/404 要不要也管细心的朋友会问登录过期401、资源不存在404呢项目约定上BusinessException的code是可扩展的——比如设备不存在你可以抛new BusinessException(4040, 设备不存在)统一走code业务码前端按码提示HTTP 状态码仍是 200。这种业务码优先风格在前后端分离 小程序里很常见小程序端统一只看Result.code不依赖 HTTP 状态码分支。当然你也可以让 Spring Security 这类安全框架直接返回 HTTP 401那属于另一套鉴权链路。Demo 阶段URM Ultra用业务码表达业务失败足够清晰也避免了 HTTP 状态码和业务的语义打架。七、小结与避坑清单业务里只抛不抓异常沿调用栈抛到全局处理器业务主干才干净定义BusinessException(code, message)给错误一个业务身份前端按code做差异化提示RestControllerAdvice 多个ExceptionHandler按具体优先、兜底最后的顺序分层接异常BusinessException→ 原样Result.error(code,msg)校验异常 → 统一 400其它 → 兜底 500 打日志兜底 handler 务必打log.error否则线上异常就消失了你永远不知道系统炸过好处的本质异常翻译集中、响应结构恒定、业务与错误处理解耦。下一篇我们顺着MethodArgumentNotValidException这根线把参数校验讲透——看看NotBlank是怎么在进业务之前就把脏数据挡在门外的。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号