
Spring Boot 这个名字在 Java 后端开发圈子里基本已经是事实上的标配了。很多从 SSM 传统封装方式转过来的朋友第一次打开一个 Spring Boot 项目时往往会有一种既熟悉又陌生的感觉——代码量明显变少了但启动类、自动配置、Starter 这些概念又让人摸不着头脑。这篇文章是我的 Spring Boot 学习系列第一篇会从最基础的问题讲起Spring Boot 到底解决了什么怎么从零创建一个项目以及项目跑起来之后配置文件该怎么看、怎么改。内容沿用的是我在实际项目中从 IDE 到命令行、从本地验证到多环境切换这一整套完整路径每一步都能直接照着做。适合的读者是已经掌握 Java 基础、用过 Maven但对 Spring Boot 只有一个模糊印象的人或者你已经在写 Spring Boot 项目却对自动配置一直停留在一知半解状态的开发者。这一篇不追求讲得多深重点是帮你把地基打扎实把开发中最常见的坑提前踩掉。1. Spring Boot 究竟解决了什么问题1.1 传统 Spring 开发的痛点要理解 Spring Boot最好先回到它出现之前的日子。早期写 Spring 项目Step 1 就是要准备一堆 XML 配置文件。数据源配一套、事务管理器配一套、视图解析器再配一套环境换一下又要重新调整连接参数。我记得最早接触这类封装模式时光是把模板项目的配置文件翻译成自己的业务场景就花了大半天而且大部分时间不是在写业务而是在跟配置较劲。依赖管理也相当头疼。不同模块版本之间的兼容性很多时候只有真正跑起来才能发现今天升级一个包明天就可能冒出一个莫名其妙的类冲突。更不用说部署环节——需要先把代码打成 war 包再丢到独立的 Servlet 容器里容器版本、启动参数、部署路径都要提前维护好。这些痛点本质上是框架把选择权都留给了开发者但没有提供足够可靠的默认值。1.2 Spring Boot 给出的解题思路Spring Boot 的核心思想用一句话概括就是约定优于配置。它不要求你彻底放弃配置而是把大量重复性的、本可以合理推断的配置给自动化掉。体现在三个方面起步依赖Starter引入spring-boot-starter-web就自动带入 Web MVC、内嵌容器、Jackson 等关联依赖版本由 Boot 统一管理解决依赖地狱。自动配置AutoConfiguration根据 classpath 上有没有对应类、配置文件中有没有相关属性、容器里有没有特定 Bean自动装配出合适的默认组件。你在配置文件里写server.port8081它就会自动感知并重新绑定内嵌容器端口。内嵌容器Web 项目打包后自带 Tomcat直接java -jar app.jar就能跑不再依赖外部容器。这三者叠加带来最直接的感受就是建一个可运行的 Web 项目从以前动辄个把小时的复杂配置缩短到几十秒。对团队协作来说新增成员上手成本也低了很多——不需要先研究整套内部封装规范只需要知道几个约定位置。需要说明的是Spring Boot 并没有和 Spring分家它仍然是基于 Spring 框架的生态层。它做的是在 Spring 之上提供了一套更便捷、更工程化的使用方式。这一点对后续阅读自动配置源码特别重要。2. 创建 Spring Boot 项目的几种常见方式2.1 官方初始化页面最标准的方式是访问 Spring 官方提供的项目初始化页面。这一步你需要选择配置项我的建议构建工具Maven 优先团队协作最通用语言JavaSpring Boot 版本选择稳定版一般选已发布几个月的版本避开最新刚发布的 RCGroup / Artifact按公司命名规范填写如 com.yourteam / demo-start依赖初期只勾 Spring Web其他需要再加选完直接点击生成下载下来就是一个可以导入 IDE 的完整工程。这里有个容易被忽略的点生成时尽量保持默认的 Packaging 为 Jar。虽然 Spring Boot 也支持 war 包但既然内嵌容器是核心特性没必要退回到传统部署方式。新版官方初始化页面里还提供了依赖可视化面板勾选后可以直观看到将引入哪些组件。2.2 使用 IDE 内置向导创建现在主流的 Java IDE 都内置了 Spring Initializr 向导操作完全图形化。我的习惯是在本地用 IDE 创建因为生成后可以直接跑起来看效果中间少一次下载压缩包、解压、导入的步骤。选择 Spring Web、设置好 Maven 坐标点击下一步即可。有一点要提醒IDE 里的初始化版本列表有时会滞后于官网。如果某个新版本的特性你急需用到建议还是官网生成后导入不要强制本地版本刷不出来。2.3 手动搭建 Maven 骨架这种方式虽然用得少但我强烈建议每个学习 Spring Boot 的人至少手动搭一次。新建一个空 Maven 工程在pom.xml中引入parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build然后自己创建启动类。这样走一遍你会真正理解Starter 到底引入了什么、parent 管理了什么版本而不是只把它当作向导里的一个勾选项。3. 项目结构逐层拆解3.1 标准目录规则一个默认生成的 Spring Boot 项目目录结构非常固定src ├─ main │ ├─ java │ │ └─ com/example/demo │ │ ├─ DemoApplication.java │ │ ├─ controller/ │ │ ├─ service/ │ │ └─ mapper/ │ └─ resources │ ├─ application.properties │ ├─ static/ │ └─ templates/ └─ test └─ java └─ com/example/demo这里最重要的约定是两层src/main/java下按包名倒置组织代码启动类放在最外层根包下。为什么因为SpringBootApplication默认扫描的是它所在包及其子包。如果启动类放在com.example.boot而 Controller 放在com.example.controller不在同一个包层级下Controller 就不会被扫描到。这是很多初学者遇到404 找不到接口的根源之一。src/main/resources放资源文件静态页面放static/模板页面放templates/配置文件默认是application.properties或application.yml。3.2 启动类里那三个注解启动类上的SpringBootApplication是一个组合注解它等效于下面三个SpringBootConfiguration也是一个Configuration的派生注解相当于把一个类标记为配置类。EnableAutoConfiguration开启自动配置机制。这是 Spring Boot魔法的入口。ComponentScan默认扫描启动类所在包及子包下的组件。入门阶段你不需要天天面对这三个注解但至少要建立启动类其实是一个配置类的意识。后续当你想要做自定义自动配置、或者排查 Bean 没有被加载的问题时第一个想到的就是EnableAutoConfiguration的具体行为。我在早期调试时踩过这样的坑项目跑起来了但访问任何接口都报 404。检查了半天最后发现是 Controller 所在包名和启动类不在同一父包下SpringBootApplication默认扫描范围覆盖不到。这是一个纯目录约定导致的错误属于 Spring Boot 项目里出现频率最高的新手问题。4. 配置文件怎么理解和配置4.1 application.properties 与 application.yml 二选一Spring Boot 支持两种配置文件格式传统的.properties和树状的.yml。两者底层是同一套配置模型完全可以等值替换。server.port8080 spring.application.namedemoserver: port: 8080 spring: application: name: demo我的建议是新项目一律用 YAML。原因很简单嵌套结构清晰、注释方便、同一类配置天然分组。尤其是数据库、Redis 这类多级配置.properties写出来一长串前缀阅读成本明显高于.yml。但如果你的团队熟悉传统方式或者引入的某个旧库提供了.properties模板迁不迁移都不影响功能Boot 内部会按类名统一解析。4.2 常见配置项的用途速查配置项作用示例server.port服务端口8080spring.application.name应用名微服注册时很有用order-serviceserver.servlet.context-path统一访问路径前缀/apispring.profiles.active激活的配置环境devspring.datasource.*数据源配置连接地址、账号密码logging.level.*指定包或类的日志级别logging.level.org.springframeworkinfo这里有一点要特别说明配置文件里的值最终会通过 Spring Boot 的配置绑定机制赋给具体组件的某些属性。例如server.port其实对应的是内嵌 Web 服务器自动配置类中的属性。它不是直接把配置塞给系统而是经过一个叫Environment的抽象提供的。4.3 自定义配置的两种姿势大部分自定义业务配置我会分成两类简单取值型、映射成对象型。取值型使用ValueRestController public class HelloController { Value(${demo.author}) private String author; GetMapping(/author) public String author() { return 作者: author; } }对应的 YAML 是demo: author: 张三映射成对象型使用ConfigurationProperties更推荐在配置项较多时使用Component ConfigurationProperties(prefix demo) public class DemoProperties { private String author; private Integer maxSize; // getter / setter }此时demo.author、demo.max-size都会自动绑定到对应字段。注意开启ConfigurationProperties后需要保证字段有 setter 方法或者使用ConfigurationPropertiesScan配合构造器绑定。Spring Boot 3.x 还引入了ConfigurationProperties与 Java 记录类型的简化写法入门阶段先用标准方式即可。4.4 多环境配置一定不能省实际开发中本地、测试、生产的配置差异很大。如果每次发布前手工改一遍端口、数据库地址迟早会在某个深夜把生产环境某些配置覆盖成测试值。推荐的做法是用 profile 拆分# application.yml spring: profiles: active: dev# application-dev.yml server: port: 8080 demo: author: dev环境# application-prod.yml server: port: 80 demo: author: prod环境启动时可以指定环境覆盖文件里的默认值java -jar demo.jar --spring.profiles.activeprod配置文件加载优先级在团队协作时很值得记住。优先级从高到低大致为命令行参数 Java 系统属性 操作系统环境变量 外部配置文件jar 包所在目录的config/或者同级目录 内部配置文件。也就是说即使application.yml里写了server.port80部署时用java -jar demo.jar --server.port8081依然会覆盖它。这个机制在开发时可以不用理会但部署和排查线上问题时往往是关键突破口——某些场景下运维没法改 jar 包但只要把一份application-prod.yml放到应用同级目录内部配置会被外部覆盖这个特性方便了按机器做差异化配置。4.5 占位符、随机数等小技巧配置文件中还可以引用已有环境变量也可以生成随机数server: port: ${PORT:8080}上面意思是优先取环境变量PORT不存在则用默认8080。这在容器部署场景下非常实用。demo: secret: ${random.uuid} max-size: ${random.int(1024, 65536)}生成启动即随机的密钥或参数对验证逻辑、模拟并发等场景很省事。5. 第一个接口的完整落地流程5.1 从启动类到 Controller 的最小链路创建好项目后构造一个最简单的接口package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class HelloController { GetMapping(/hello) public String hello() { return Hello Spring Boot; } }然后在 IDE 里执行main方法控制台会输出 Spring Boot 的 Logo 和启动日志看到Tomcat started on port 8080就代表成功了。打开浏览器访问http://localhost:8080/hello返回Hello Spring Boot项目闭环跑通。这里用RestController而不是Controller是因为RestController直接返回字符串或对象并自动转 JSON适合前后端分离接口场景。如果写成Controller默认会去找对应的视图模板而不是返回内容。5.2 使用 Maven 命令启动很多人习惯在 IDE 里直接跑但命令行打包能力才是上线时必须掌握的。先打包mvn clean package生成的目标 jar 文件在target/目录下然后执行java -jar target/demo-0.0.1-SNAPSHOT.jar如果端口被占用也可以这样java -jar target/demo-0.0.1-SNAPSHOT.jar --server.port8081mvn命令打包时spring-boot-maven-plugin会把所有依赖合并进一个可执行 jar这就是 Spring Boot 部署如此轻量的根本原因。强制执行mvn clean package时若提示测试失败可以先加-DskipTests跳过但这只是图快正式环境我不建议这么做测试还是要跑。5.3 调试利器devtools 热更新开发中我强烈建议加上 devtools 依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-devtools/artifactId optionaltrue/optional /dependency加了它之后代码改动后 IDE 里重新编译应用会自动重启省去手动重启。你改了 Controller 里的一个字段值不需要整站重启就能直接验证。不过要记一个坑devtools 对依赖配置文件的变化有些情况不会触发自动重启改pom.xml、application.yml后偶尔需要手动重启才生效。生产环境也不需要打进去optional已经确保它不会传递发布。6. 常见问题排查与避坑实录6.1 端口被占用现象启动时报Port 8080 was already in use。排查思路先确认是不是自己的另一个项目占了端口。用命令查占用进程netstat -ano | findstr 8080 # Windows lsof -i :8080 # macOS/Linux最省事的方案是直接改当前项目端口server.port8081。如果你希望在启动命令里临时换端口而不改动文件也可以用--server.port8081的方式配置文件、命令行、环境变量这几处的改动优先级我前面已经讲过。6.2 Controller 报 404这是最常见的代码没问题路径不对问题。原因主要有三类启动类的包路径层级比 Controller 低导致自动扫描范围缺失。Controller 类没有加RestController或ControllerSpring 不识别。访问路径拼错比如方法上写了/hello但访问时带了额外前缀。排查时可以打开spring的日志级别看扫描情况logging: level: org.springframework: debug日志里会打印出自动配置、Bean 注册等过程有助于判断扫描范围。查看更多信息还可以借助 Actuator 的mappings端点但那是后话了。6.3 配置文件不生效有时改了application.yml重启后却感觉没生效。第一反应看是否有多个配置文件同时存在。Spring Boot 对application.properties和application.yml的加载顺序是前者优先于后者如果一个项目里两种文件同时存在properties 里的值会覆盖 yml 里的相同项。这种双配置很坑团队协作时容易相互踩到建议项目里只保留一种。还有一个隐蔽原因配置文件编码格式错误。YAML 严格区分空格与制表符缩进用 Tab 会导致解析失败。某次我帮同事排查启动日志没有任何报错但配置项全是默认值最后发现是application.yml里的spring:和profiles:缩进不一致导致整个层级被解析成不同父级。YAML 缩进的一点点偏差后果可能隔了几个配置属性才体现。6.4 JDK 版本与 Spring Boot 版本匹配Spring Boot 3.x 要求 JDK 17 及以上这是很多从 Boot 2.x 升级项目的人最容易遇到的问题。如果本机装的是 JDK 8强行用 Boot 3.x 会直接启动失败提示UnsupportedClassVersionError或 Maven 编译阶段即报错。同样Spring Boot 2.x 对应 JDK 8/113.x 对应 17/21新版本甚至更高。在初始化项目时要先在官网看版本说明再检查本地java -version避免一上来就走弯路。团队项目尤其要统一 JDK 版本和 Boot 版本避免每个人跑出来的行为不一致。6.5 热部署失效时的处理顺序devtools 不生效我的处理顺序是确认依赖确实引入了不是optional配错导致没进入编译。确认 IDE 里开启了自动编译许多 IDEA 版本不需要手动但有些快捷键冲突会关闭。查看控制台有没有LiveReload server started相关信息。实在不行就手动重启一次devtools 只是开发阶段的提效手段不要为了它花太多时间。6.6 常见问题速查表现象可能原因解决思路启动即端口占用本机已有服务占用 8080改端口或杀掉占用进程接口 404启动类扫描范围不对把启动类放在根包Controller 放其子包接口 500未引入 Web 依赖在 pom 中加spring-boot-starter-web配置值未变化properties 与 yml 同时存在统一只保留一种配置解析失败YAML 缩进/编码问题用 IDE 的 YAML 校验不使用 Tab 缩进启动失败类版本错误JDK 和 Boot 版本不匹配按官方表格检查对应关系mvn package后运行报错缺少主类配置检查启动类位置确保是SpringBootApplication7. 我自己的学习和使用心得第一次真正理解 Spring Boot是因为我当时断断续续见了很多文档却没有动手做过一个完整的项目。后面花了两个晚上硬把一个带数据库读写的小管理系统用 Spring Boot 重构了一遍才算把自动配置这四个字内化。那种原来框架替我做了这么多事情的豁然感是看多少篇博客都换不来的。所以这个系列虽然叫学习笔记但我写下来的都是自己真正跑过的路径。如果你正在入门建议按顺序完成先不写任何业务把带 web 依赖的空项目跑起来然后写一个带请求参数的接口再尝试连接一次数据库或 Redis最后切换两套 profile 看日志变化。把这个流程跑顺Spring Boot 的基础大门就已经打开了。后面还有配置绑定细节、日志系统、数据访问、拦截器统一响应等专题我会按实际工作中高频用到的深度继续写。这张基础地图就当是你开始动手前的最靠谱支点。