
踩坑不是坏事但重复踩同一个坑就是浪费生命。我见过太多团队在SpringBoot项目刚起步时用一周时间搭骨架然后用三个月时间给当初的草率决定还债。今天这篇不谈理论直接把我自己反复踩过、也看别人反复踩过的五个坑摊开来讲。每个坑都配了具体的避坑姿势希望你能绕开它们。第一坑目录结构按“层”分包而不是按“域”分包很多新手甚至老手一上来就建四个包controller、service、dao、entity。看着清爽项目一大就完蛋。订单相关的逻辑散落在二十个目录里改一个需求要跨五个包找代码。分层是逻辑概念分包是物理组织两者强行一一对应只会让代码腐烂速度翻倍。正确的做法是按业务域分包。比如order域下面放OrderController、OrderService、OrderRepository、OrderEntity整个订单相关的东西内聚在一起。内聚性是代码可维护性的第一指标不是包名多漂亮。如果担心跨域调用用接口隔离而不是用物理位置强行拆分。另外包名不要用common之类的大杂烩。我见过某个项目的common包里塞了工具类、常量、异常定义、配置类、还有几个过时的Service。这不是公共模块这是垃圾场。真要有共享逻辑按领域命名拆分比如order-api、user-api或者干脆通过独立jar管理。避坑建议新建项目时先花半小时画出业务域地图然后按域建包。如果项目已经分层了也别急着大重构可以逐步把新需求写到新域包里老代码慢慢迁移。迁移不是一次性的工程量而是一个持续性的卫生习惯。第二坑配置文件一把梭环境切换全靠注释SpringBoot的application.properties有人一辈子只用这一个文件。开发环境、测试环境、生产环境的数据库地址、Redis密码、日志级别全挤在一起。要切换环境手工注释掉一段再取消注释另一段。用注释管理环境配置等于在悬崖边走钢丝早晚掉下去。正确姿势是Spring Profile。弄三个文件application-dev.yml、application-test.yml、application-prod.yml然后通过spring.profiles.active激活。这还不够敏感信息绝不能明文提交到Git仓库。用环境变量或配置中心比如Nacos、Apollo来注入生产密码把application-prod.yml里的密码写成${DB_PASSWORD}让运维在部署时注入。配置和代码分离是专业和业余的分水岭。还有个大坑把配置值直接塞进注解里。比如Value(${order.timeout:30})散落在各处改一个配置要全局搜索。建议把所有配置项集中在一个Properties类里用ConfigurationProperties绑定统一管理顺便还能做类型校验。配置不是代码的边角料它是系统的显式边界边界必须整洁。避坑建议从第一天起就启用Profile每个环境独立文件。生产环境的敏感值一律用占位符外部注入。加一个application-local.yml用于本地快速调试别动公共配置。第三坑事务只加在方法上连数据库都笑了有同学写Transactional直接标在Controller方法上。结果一个请求里查了个列表、调了远程接口、再写库整个流程被一个巨大事务包着数据库连接被长时间占用高并发一来连接池直接打爆。事务不是金钟罩它是锁锁的范围越大系统吞吐越差。更隐蔽的坑是在同一个类内一个方法调用另一个带Transactional的方法事务注解失效。因为Spring的事务通过代理实现内部方法自调用绕过了代理就像你请了个保安但自己从后门溜进去了。很多人排查半天最后发现是这个原因哭笑不得。还有事务里别做远程IO。比如在事务里调RPC、发消息、写文件一旦远程服务慢事务就长时间持锁其他请求全卡住。事务只应该包含必须原子化的数据库操作其他动作一律移到事务外。避坑建议事务加在Service层的公开方法上且方法粒度要小。如果多个写操作必须原子化就拆分Service方法把事务边界放在最外层调用处。在需要自调用的场景要么拆分到不同类要么用TransactionTemplate手动控制。不要迷信注解要理解代理的脾性。第四坑异常处理全靠try-catch到处都是吞异常打开一个项目满屏try { ... } catch (Exception e) { e.printStackTrace(); }。然后呢日志里一片红业务数据却已经错乱了。吞异常是最隐蔽的bug工厂它让失败变得无声无息直到用户投诉才发现。另一种极端是每个方法都throws Exception。Controller方法上写着throws ExceptionService接口也throws Exception结果全局异常处理器根本接不住因为异常签名在编译期就声明出去了到了Controller层没人处理直接返回500。这等于把错误处理的责任甩给最上层而最上层根本不知道底层发生了什么。正确的做法是定义统一的业务异常如BizException带错误码和错误消息。在Service层发现业务规则不满足时抛出BizException。再写一个RestControllerAdvice全局异常处理器针对BizException返回对应错误码HTTP响应针对参数校验异常返回400针对其他未捕获异常统一记录日志并返回500。前端拿到的错误信息是结构化的JSON而不是一堆堆栈。还有一条狠点的原则只在能处理异常的地方捕获异常否则就抛出去。你catch它就是为了打印一行日志那不如不catch让全局处理器去兜底。每一次catch都必须有业务含义要么降级要么重试要么转成业务错误。否则就是代码里的口香糖黏糊糊还恶心。避坑建议建一个GlobalExceptionHandler把异常分类处理。业务异常直接转JSON错误码技术异常记录完整堆栈后返回通用错误。在Service层校验失败早抛异常别返回null或空对象让调用方去猜。第五坑依赖注入用Autowired成瘾也没管循环依赖SpringBoot太香了往字段上写个Autowired对象就来了。但随之而来的是字段注入无法让你意识到类之间的耦合直到某天启动报The dependencies of some of the beans form a cycle。你一看A依赖BB依赖CC又依赖A三人转圈圈。Spring可以处理单例的循环依赖但处理的方式是缓存半成品对象这东西在构造函数注入时完全没戏。更好的选择是构造器注入它强制你在创建对象时把依赖说清楚类需要什么一眼可见。而且构造器注入天然避免循环依赖——如果有循环编译期或启动期直接报错逼你重构而不是默默用加缓存逻辑兜底。依赖应该是显式的而不是像魔术一样从字段里冒出来。另一个坑在构造函数里做了太多事。比如在构造器里调用远程服务、加载大量数据这会让Bean初始化变慢而且遇到代理失效等诡异问题。构造器只做简单的赋值和合法性检查其他事放到PostConstruct或者ApplicationRunner里做。避坑建议新代码一律用构造器注入。也可以使用RequiredArgsConstructor配合final字段省掉手写构造器。如果发现循环依赖停下来思考一下设计把被循环的那个依赖拆出去。依赖图应该是DAG有向无环图不是蜘蛛网。上面这五个坑每一个我都亲眼见过它们如何把一个新项目拖入泥潭。但更有价值的不是坑本身而是坑背后的思维模式配置不是临时凑合的结构不是顺手堆的事务不是越宽越好异常不是越吞越安全依赖不是越隐式越优雅。SpringBoot是一个极其宽容的框架你做什么它都能让你跑起来但等到流量大了、团队大了、交付节奏快了那些当初的偷懒和妥协都会变成定时炸弹。好的架构不是一步到位的而是从第一天起就坚持正确的小事。踩坑不可怕怕的是不知道自己踩了坑还觉得是SpringBoot的锅。如果你现在正准备从零搭项目希望你把目录结构、配置管理、事务边界、异常处理和依赖注入这五件事当成项目的第一块地基。地基稳了上面的每一行代码都有意义。地基歪了后面的所有优化都是给危房刷漆。最后送你一句话优秀程序员不是不踩坑而是每个坑只踩一次然后把坑填平立个牌子告诉后来人——这里有过坑绕道。希望这篇文就是你前面的牌子。