从零开始搭建SpringBoot项目:我的实战笔记

发布时间:2026/8/8 6:07:32
从零开始搭建SpringBoot项目:我的实战笔记 新建一个空文件夹我盯着屏幕足足三分钟。这是每一个从零开始搭建SpringBoot项目的人都会有的时刻——光标停在空白处脑子里却装满了“自动配置”“约定优于配置”“内嵌服务器”这些概念。可真正动手时你会发现这些概念帮不了你因为从零开始的本质是面对未知而未知的第一反应永远是束手无策。环境的坑比想象中早得多。JDK版本、构建工具、IDE版本每一个都可能让你铩羽而归。SpringBoot2.x还兼容JDK8SpringBoot3.x则强制要求JDK17及以上。如果团队里有人用Java8有人用Java11那就要小心了。SpringBoot的版本决定了你未来一年要踩的坑的数量。我在从2.7迁移到3.x时光是javax到jakarta的包名替换就改了几十个文件还有一个老库始终不兼容最后只能忍痛替换。所以开始之前先统一团队的环境再谈其他。不然项目还没动手光是Java版本问题就能耗掉你半天。初始化项目的方式很多我推荐Spring Initializr。在网页上选择构建工具、语言、SpringBoot版本再勾上需要的依赖点击生成一个项目就诞生了。但你可能会惊讶地发现即使什么依赖都不选生成的项目里也自带spring-boot-starter。SpringBoot的骨架里藏着“默认值”的哲学但你若不理解这个默认值后面的一切都会变得诡异。比如为什么启动类要放在最顶层目录为什么主类上要有SpringBootApplication这些看似约定俗成的东西背后是有原因的。不理解它们你很容易在后续的配置扫描中迷失方向。项目骨架谁在替你负重前行打开pom.xml你会看到一段长长的依赖列表。SpringBoot的核心不是代码而是依赖管理。一个starter背后是数十个库的兼容性调试。你不需要声明每个库的版本因为父POM已经为你锁定了全局。这种便利也埋下了隐患当你引入第三方starter时版本冲突会像幽灵一样冒出来。我曾在项目里同时使用Redis和MongoDB的starter它们各自传递依赖了不同版本的spring-data-commons结果启动时一直报NoSuchMethodError整整排查了一天。依赖管理是SpringBoot的魔法也是它的恶魔。所以每次新增依赖我都会用mvn dependency:tree看看实际生效的版本这个习惯救过我很多次。第一个接口简单的背后有陷阱写一个RestController添加一个GetMapping(/hello)接着点击启动。看着控制台滚动出Spring的Logo心里升起一股“成了”的感觉。但别忘了跑通一个接口只代表环境成立不代表质量可靠。真正的麻烦往往在启动日志的最后一行端口被占用、找不到视图解析器、路径映射冲突。我遇到的最低级却最耗时的错误是在application.yml里写了中文注释而文件编码不是UTF-8导致应用启动直接失败。SpringBoot默认用UTF-8读取配置但Windows下文件编码容易被IDE悄悄改掉。配置文件的编码问题往往比代码错误更隐蔽也更难查。热部署与开发体验效率的博弈写代码时最烦的就是改了代码要重启。SpringBoot提供了spring-boot-devtools依赖可以实现代码热更新。但别期望太高它默认只是重启应用不是真正的热替换。而且热部署带来的便利有时会被它引入的类加载器问题抵消。我见过有人为了热部署不断修改IDE设置最后项目跑不起来才发现是缓存作祟。更可靠的做法是使用JRebel或Spring DevTools但它们并非万能。开发体验很重要但不要把开发效率的赌注押在工具上工具只是辅助理解运行机制才是根本。配置管理处处是坑随着项目变大一个配置文件不够用了。dev、test、prod三套环境三个文件。配置是SpringBoot最容易出鬼的地方没有之一。spring.profiles.active这个属性很多人喜欢写在application.yml里但它通常需要在启动参数或环境变量中指定否则你部署到生产环境时会惊讶地发现它加载的是开发配置。另一个坑是占位符${...}。如果配置里引用的key不存在启动会立刻报错但如果key存在值却是空字符串你就会得到莫名其妙的NullPointerException。更复杂的是配置优先级命令行参数覆盖环境变量环境变量覆盖配置文件而application.properties和application.yml同时存在时前者优先级更高。别问我为什么要知道这些全是血泪。数据库集成连接池与事务数据库是绕不开的。JdbcTemplate、MyBatis、Spring Data JPA各有各的支持者。数据库连接池永远不要用默认配置。HikariCP默认最大连接数是10如果你的接口响应慢10个连接很快就会被占满后续请求全部排队。我曾在生产环境遇到一次事故就是因为一个慢SQL把连接池耗尽而我没有设置超时时间。调整连接池参数并不复杂但很多人根本不知道有这些参数。至于Transactional这个注解看似简单实际上陷阱重重同类内部方法调用时事务会失效异常被try-catch吞掉时事务也会失效在另一个线程里调用时更别提了。事务的失效方式比你想象的多得多。项目分层别被“简单”骗了SpringBoot的Controller、Service、Repository三层结构被定义为标准但标准之外还有大量细节。Controller里应该只处理HTTP请求业务逻辑全部下沉到ServiceRepository又分接口和实现MyBatis里还有Mapper。记住分层的意义不是代码好看而是责任边界清晰。我见过很多人图省事直接在Controller里写SQL查询当时是爽了可三个月后要加一个缓存就只能把整个Controller重写。架构上的懒惰最终会用时间偿还。更关键的是分层设计让单元测试变得容易。如果所有逻辑都堆在一起你连一个像样的测试都写不出来。前后端联调跨域与统一响应当后端接口写好了前端一跑第一个报错往往是跨域。跨域问题是联调时最磨人的问题你甚至不需要懂CORS协议也得知道怎么用SpringBoot解决。简单的做法是写一个配置类实现WebMvcConfigurer重写addCorsMappings。但要注意如果加了Spring Security这个配置可能不够还需要在Security配置里放行OPTIONS请求。另一个容易被忽略的是统一响应体。我习惯用一个Result类包装所有接口返回里面包含code、message、data。这看似增加了工作量但能让你从一堆奇葩的前端解析逻辑中解脱出来。统一响应体不是说每个接口都返回一样的样子而是让错误和成功有了固定的语言。日志被轻视的救命稻草大多数人第一次启动SpringBoot时只看控制台上的红色报错然后就不管了。日志是你在生产环境唯一的眼睛但你通常要等到出事了才想起配置它。SpringBoot默认使用Logback但默认配置只输出到控制台没有文件也没有滚动策略。我遇到过线上问题没有任何异常只是接口响应变慢要不是我配置了慢SQL日志根本不可能定位到那条缺失索引的查询。日志不是可有可无的输出而是系统生命活动的记录仪。合理的做法是为每个关键流程打上日志不打印敏感信息使用MDC在日志里带上请求ID这样排查问题时才能串联起整个调用链。日志级别也要分环境dev用DEBUGprod用INFO。测试与打包最后的防线单元测试是质量的底线但SpringBoot的测试体系也容易误导人。SpringBootTest会把整个应用上下文拉起来非常慢而且任何一个Bean初始化失败都会牵连所有测试。测试不是越多越好而是越准越好。更好的做法是用WebMvcTest或DataJpaTest这样的切片测试只加载需要的部分速度飞快。至于打包SpringBoot默认生成可执行jar内嵌Tomcat。但如果你要部署到外部服务器就得改用war包并排除内嵌Tomcat。部署方式必须在项目初期就决定否则改起来到处都是麻烦。另外打包产物里最好带上完整的版本号和时间戳否则出了问题你根本不知道线上跑的是哪个版本。常见坑与升级之路每个从零开始的SpringBoot项目都会经历相似的痛苦版本不兼容、配置错乱、依赖冲突、日志缺失……所谓的实战笔记其实就是一份踩坑清单。而最大的坑是对SpringBoot的过度信任。它确实简化了开发但并没有消除复杂性只是把复杂性藏到了你看不见的地方。当你需要自定义starter、深度调优线程池、处理分布式事务时你会明白SpringBoot仅仅是一个起点。它给不了你架构能力也给不了你业务判断力。但这些挫败感恰恰是成长的催化剂。那么怎样才能少踩坑我的回答是多看官方文档多读报错日志多写小demo去验证想法。每一个异常堆栈都是一次学习机会不要急着复制粘贴。当你把一个异常真正弄懂它就会成为你的垫脚石。从零开始搭建SpringBoot项目最大的收获不是跑通了程序而是你开始理解它所依赖的整个Java生态。这也是为什么我至今依然喜欢从零开始一个新项目——每一次重复都是一次重新审视。现在重新创建一个空项目你还会犹豫吗也许还会但你已经知道犹豫不决才是最大的敌人。