把IDE打开,手指放到键盘上,心里默念“这一次我一定要搞懂每一步”,然后开始。很多人学Spring Boot的第一天就栽在“照抄教程但不知道自己在抄什么”上,复制粘贴跑通一个hello world,第二天全部忘光。真正值得收藏的步骤,不是菜谱,而是让你建立心智模型的地图。从零搭建第一个项目,本质不是“配置一个框架”,而是理解一个Spring Boot应用在启动那一刻,到底发生了什么。
先把话说透:Spring Boot不是编程语言,而是一套“帮你省掉配置地狱”的约定优于配置方案。它把Tomcat、Spring MVC、Jackson、数据源这些组件打包成“起步依赖”,让你用几行代码就能拥有一个能跑起来的Web服务。但你如果不知道这些约定藏在哪、怎么被激活,项目一报错就会懵。所以,我们按“手把手+为什么”的方式走一遍。
先决定用哪种方式:IDEA向导还是Spring Initializr
新手往往纠结:我是去start.spring.io网站下载压缩包,还是在IDE里直接新建?答案很简单——用Spring Initializr,但一定要打开浏览器手动操作一次,别让IDE替你全程隐身。因为你在网站上能看见所有依赖选项,能直观理解“Web”“JPA”“Validation”这些词意味着什么。IDE向导虽然方便,但容易让你跳过思考。
打开start.spring.io,左侧填Group(一般是公司域名倒写,比如com.example)、Artifact(项目名,比如first-demo),右侧选构建工具(推荐Maven)、语言(Java)、Spring Boot版本(选稳定版,别选Snapshot),下面依赖框里至少勾选Spring Web。然后点击Generate,得到一个zip包。解压后,用IDEA以Maven项目方式打开,等右下角进度条把依赖下载完。
这一步最大的坑不是配置,而是网络。Maven中央仓库在国内经常慢到让人怀疑人生。如果你不想在“下载依赖”上浪费半小时,立刻去配置阿里云镜像。找到用户目录下的.m2/settings.xml,没有就新建一个,加上<mirror>指向aliyun.com的仓库地址。收藏任何教程,都不如收藏一份能让你流畅拉取依赖的settings.xml。这个细节决定了你后续学习的心情。
看懂了pom.xml,你就赢了一半
项目打开后,别急着写代码。先打开pom.xml。这个文件是Maven的“施工图”,也是Spring Boot给你递来的第一张名片。你会看到父项目是spring-boot-starter-parent,它锁定了所有依赖的版本,这就是“依赖管理”的核心——你不需要再为各种库的版本冲突熬夜,因为Boot已经帮你测试过哪些版本能和谐共处。
再看你勾选的spring-boot-starter-web。发现没有?它没有版本号。这就是父项目的功劳。而这个starter内部,又传递依赖了Tomcat、Spring MVC、Jackson等一大堆库。你只加了一个,实际上牵动了整个Web开发基础全家桶。这才是Spring Boot真正让你“起飞”的东西——起步依赖,一把梭。
有些教程会教你手动引入spring-boot-maven-plugin,这个插件负责把项目打成可执行的jar包。别小看它,没有这个插件,你在命令行敲java -jar时只会得到“没有主清单属性”的报错。所以,看到pom.xml里有这个插件,心里要默默感谢一下。
启动类:那个带main方法的“神主牌”
Spring Boot的项目结构里有且只有一个类带@SpringBootApplication注解,它通常放在主包下,是入口。这个注解是“三合一”:里面藏着@SpringBootConfiguration(表示这是一个配置类)、@EnableAutoConfiguration(开启自动配置)、@ComponentScan(扫描当前包及子包的组件)。通俗地说,这个注解是教Spring Boot“从哪里开始认识你的代码”。如果你把启动类放错位置,比如放进子包,那么它默认扫描不到同级包之外的其他组件,你就会遇到“Controller找不到”的神奇问题。
main方法里只有一行:SpringApplication.run(FirstDemoApplication.class, args);。别小看这行,这就是Tomcat被悄悄启动、DispatcherServlet被注册、各种AutoConfiguration开始按条件装配的“总开关”。SpringApplication.run返回一个ApplicationContext,那是整个IoC容器的心脏。你现在不用深究,但记住这一刻,Boot已经把内嵌的Web服务器拉起来了,你不需要再独立装一个Tomcat。
写第一个Controller:让请求有个去处
在启动类同级下建一个controller包,里面新建一个类HelloController。代码很简单:
@RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, Spring Boot!"; } }
@RestController是@Controller加@ResponseBody的组合体,意思是“这个类的每个方法的返回值,直接写入HTTP响应体,而不是返回一个视图名”。很多新手在这里踩坑:不加@ResponseBody,返回字符串会被Spring MVC当成视图名去找模板,最终报404。所以记住,做前后端分离或简单接口,直接用@RestController最省心。
@GetMapping指明“GET请求,路径为/hello时,执行这个方法”。你现在的项目已经是一个能响应HTTP请求的微服务了。然后右键启动类,点Run。看控制台输出,注意出现“Tomcat started on port(s): 8080”时,你的项目就已经站在跑道上了。打开浏览器访问http://localhost:8080/hello,看到那个字符串,恭喜你完成了“从零到一”最核心的动作。
配置文件:别再写application.properties了,试试yaml
项目里有一个application.properties文件,它是默认配置。很多人直接在里面写东西,但我更推荐改成application.yml。YAML的缩进结构可读性更强,而且Spring Boot两者都支持。改端口、改上下文路径、配置数据源,都往里写。
比如想要服务器端口不是8080,就写:
server: port: 9090
有人问:“端口被占用怎么改?”这正好。能改端口,说明你已经理解了外置配置的优先级——命令行参数 > Java系统属性 > application.yml > 内置默认值。这个优先级规则是排查问题的利器。当你想覆盖配置时,先想想在哪个优先级上操作。
配置文件的另一个作用是“环境切换”。你可以创建application-dev.yml、application-prod.yml,然后在主配置里用spring.profiles.active=dev指定当前环境。环境隔离是专业项目的第一道分水岭,没有这一步,你线上用的密码都会被同事看到。
别被“自动配置”迷惑,动手看一眼它的真面目
为什么要强调“看一眼”?因为自动配置不是魔法,而是条件判断后的一堆Bean。你可以在任何地方实现ApplicationRunner接口,打印一下现有的Bean定义名称,或者访问/beans端点。更直接的方式:在启动类里加一行:
@SpringBootApplication public class FirstDemoApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(FirstDemoApplication.class, args); String[] beanNames = ctx.getBeanDefinitionNames(); System.out.println("总共定义了 " + beanNames.length + " 个Bean"); } }
运行后,你会看到几百个Bean的名字。其中很多你根本没用过,但Spring Boot根据条件注解(@ConditionalOnClass、@ConditionalOnMissingBean等)只装配了当前环境需要的。比如你没加数据库相关依赖,数据源配置就不会生效。这就是自动配置的聪明之处:不盲目,只看你pom里有没有对应的类。
理解这一点,你就不会一报错就懵。看到“No qualifying bean of type”,第一反应不是去网上复制粘贴,而是想想:这个类在classpath上吗?有没有对应的starter?有没有排除自动配置?这才是查问题的正确起点。
热部署:开发效率和心情的双重救赎
每次改代码都手动重启,还算“从零搭建”?不,那种体验太原始了。这时候加入spring-boot-devtools。在pom.xml里加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
DevTools会自动监听classpath变化,检测到代码更新后,自动重启应用。注意是“重启”不是“热替换”,但启动速度快很多,因为内嵌的容器不会冷启动几次。有人觉得热部署没多大用,但实际开发中,每省下10秒重启时间,一年就是好几小时。这还不算你因为频繁重启而被打断的心流。
另外,DevTools还会帮你在开发时关闭模板引擎缓存、打开更详细的日志。生产环境部署时,devtools会自动失效(因为打包时被标记为optional),所以不用怕带上去生产。
单元测试:别把它当成可选的“加分项”
用Boot写测试比传统SSM简单太多了。在pom里加上spring-boot-starter-test,然后写一个测试类:
@SpringBootTest class HelloControllerTest { @Autowired MockMvc mockMvc; @Test void testHello() throws Exception { mockMvc.perform(get("/hello")) .andExpect(status().isOk()) .andExpect(content().string("Hello, Spring Boot!")); } }
@SpringBootTest会加载完整应用上下文,MockMvc让你不启动Tomcat就能模拟HTTP请求。这套测试体系是Boot送给你的最被低估的礼物。很多项目上线即翻车,不是因为代码烂,而是因为没有任何保护网。哪怕你只写了三个接口,也该为每个接口配一个冒烟测试。测试代码不是给人看的,是给未来的自己留的逃生通道。
打包与运行:从IDE迈向生产环境的最后一步
在项目根目录执行mvn clean package,你会看到target目录下多了两个jar文件:一个带.jar.original后缀,另一个是可直接运行的jar。那个带.original的是只包含项目自身代码的“普通”jar,而那个不带后缀的才是Spring Boot的“可执行jar”,里面藏了所有依赖和内置Tomcat。用java -jar first-demo-0.0.1-SNAPSHOT.jar,加上--server.port=8081,就可以在任意装了JDK的机器上跑起来。没有外置Tomcat,没有环境配置,这就是云原生时代的基础形态。
如果你用的是Gradle,对应命令是./gradlew bootJar。但无论哪个构建工具,核心逻辑一致:可执行jar是Boot应用的分发单位,通过一条命令就能交付。
常见的“翻车”现场与解决思路
第一个坑:端口冲突。报错“Port 8080 was already in use”。解决方案不是把别的进程杀掉,而是用--server.port=0让系统随机分配一个端口,或者直接改配置文件。如果连这个报错都要百度,那你后面每个接口都会在“没有返回值”中挣扎。
第二个坑:Controller扫描不到。访问接口404,且控制台没有报错。检查启动类位置,是否和controller包相隔太远。记住默认扫描范围是启动类所在包及子包。要想扩大范围,就用scanBasePackages属性指定根包,但这么做容易让结构失控,不如老老实实把启动类放在最外层。
第三个坑:编码问题。返回中文变成乱码。在配置文件里设置server.servlet.encoding.force-response=true,或者更早一点,在pom里设置project.build.sourceEncoding=UTF-8。编码问题一旦出现就是连环炸,第一步就要锁定所有环节都是UTF-8。
第四个坑:编译版本不对。报Unsupported major.minor version,说明你用了过高的JDK编译,却跑在低的JRE上。排查第一步,看你的JDK版本、Maven编译器的source/target、以及运行时java -version三者是否一致。三处对齐,世界安静。
继续往前走:给你的项目再加点“灵魂”
现在你的项目能跑、能测、能打包了。但这只是一个“空壳框架”,离真正的业务系统还有距离。下一步该做什么?先去写一个带数据库的增删改查,比任何高级特性都重要。你可以加spring-boot-starter-data-jpa,配一个H2内存数据库,写一个Entity、一个Repository、一个Controller,感受一下“从HTTP请求到数据库写入”的完整链路。这一步会让你真正理解Spring Boot的“全家桶”魅力。
或者你更爱API文档?那就引入springdoc-openapi-starter-webmvc-ui,访问/swagger-ui.html自动生成接口文档。自己手动写文档?那是上世纪的事。Boot的生态就是给你一片汪洋,你只需要知道自己要往哪游。
最重要的一步:把“收藏”变成“肌肉记忆”
收藏这篇文章,一个月后你会忘得干干净净。收藏Spring Boot的官方文档、加几个靠谱的公众号,都不如你现在亲手创建一个项目。从零搭建的过程,不是“看懂的”,是“踩坑踩出来的”。你的第一个项目一定会报错,一定会出现看不懂的堆栈,没关系,那才是你真正学会的开始。
请在你自己的电脑上,按这些步骤走完一遍,然后把运行成功的截图存下来。每个运行成功的项目,都是你在工程世界留下的一座里程碑。以后你会遇到分布式、微服务、消息队列,但根永远在这第一个Hello World里。把地基打牢,之后的高楼大厦才能防风防震。SpringBoot这条路,你现在才刚踏出第一脚,而这一步,已经比大多数人强出很远。