Spring Boot 2.7.18实战:从环境搭建到Docker部署
2026/9/21 9:44:40 网站建设 项目流程

简介:一份面向SpringBoot入门者的简易示例工程,整合SpringMVC、MyBatis、Thymeleaf与FreeMarker视图解析,并演示Redis请求缓存、WebSocket以及Quartz/Spring Scheduled定时任务等常用场景,适合想快速了解SpringBoot多模块集成方式的开发者参考。压缩包共71个文件,体积仅110KB,其中以20个Java源码与20个编译后的class文件为主体,配合9个properties配置、4个HTML和2个FTL模板、4个XML映射或配置、以及少量CSS/JS资源,结构精简且类型分明,便于对照代码查看配置与页面效果。目前已有191人学习下载。通过该工程可掌握SpringBoot项目的典型目录划分、注解式MyBatis用法、Redis缓存的接入方式,以及定时任务和WebSocket的最简实现思路,可直接导入IDE运行调试,适合作为课设或自学练手的起步模板。

1. 环境准备与版本选型

1.1 为什么我建议先用 2.7.18 而不是最新版

Spring Boot 这个框架,网上教程铺天盖地,但真正动手做的时候,第一个坑往往不是代码,而是版本。如果你现在去 Spring Initializr 上选依赖,默认给你的是 3.x 甚至 4.x,搭配的 JDK 起步就是 17。而国内大量公司、教程、毕业设计用的还是 JDK 1.8,这就导致很多人下载完模板项目,一编译就是一堆看不懂的报错。

我自己踩过一次很深的坑:三年前接一个老系统的维护需求,对方环境是 JDK 1.8,我图省事直接用当时最新版 Spring Boot 3.0 搭了个新模块,结果 javax 命名空间整个变了,一大堆第三方库不兼容,最后花了两个晚上重构才跑起来。从那以后,我的习惯是:新项目如果没有特殊要求,优先选 2.7.18

为什么是 2.7.18?因为它是 Spring Boot 2.x 系列的最后一个版本,官方维护时间最长,社区反馈最充分,几乎所有第三方中间件都有对应 starter。而且它完美兼容 JDK 1.8,又支持到 JDK 17,过渡性极好。如果你想从 2.x 平滑升级到 3.x,2.7.18 也是最合适的跳板。

注意:Spring Boot 4.0 已经出现了,但它的改版幅度很大,AOP 模块、自动装配机制都有变动,很多老教程里的写法直接作废。新手学的时候容易被过时资料带偏,所以锁定一个成熟稳定版本特别重要。

1.2 开发环境一览与安装核对清单

在动手建项目之前,先把环境捋清楚。以下是我个人比较推荐的一套组合,适合绝大多数学习场景:

组件推荐版本备注
JDK1.8(或 8u202+)不要用太老的 8u191,有些新库会不兼容
Maven3.6.3 及以上3.8.x 也可以,别用 3.9+ 的某个中间版本,有镜像源问题
IDEA2022.3 及以上社区版够用,旗舰版对 Spring 的支持更完整
Docker Desktop推荐最新版用于本机部署验证,也可以换用真实 Linux 服务器

装完环境之后,建议在命令行里跑一下java -versionmvn -v,确认 PATH 都正常。我见过太多人 IDE 里能跑,命令行一执行就提示“找不到命令”,这种问题越早暴露越好。

还有一个容易被忽略的点:如果你打算用 Docker Desktop 跑 Spring Boot 镜像,需要在 IDEA 的Settings → Build Tools → Maven里确认JAVA_HOME指向的是你本机的 JDK 1.8,而不是 Docker 内部的某个 JDK。这个配置不对,后面打包镜像时会遇到各种诡异报错。

2. 项目搭建与核心配置

2.1 两种创建方式:Initializr 网页版与 IDEA 内置向导

Spring Boot 项目的创建方式其实就两类:一种是在浏览器里打开 Spring Initializr 网站生成压缩包再解压导入,另一种是用 IDEA 自带的 Spring Initializr 向导直接创建。两种方式本质都一样,但我实际用下来,推荐直接走 IDEA 内置向导,省去解压导入的步骤。

创建时需要注意这几个关键选项:

  • Group:一般填公司域名倒序,比如com.example
  • Artifact:项目名,建议小写加横杠,比如demo-server
  • Type:选 Maven 项目,Gradle 虽然也不错,但国内资料少,遇到问题不好搜
  • Language:Java
  • Packaging:选 Jar,绝大多数 Web 项目用 Jar 包部署足够

依赖这块,刚开始别贪多,只勾选Spring WebSpring Boot DevToolsLombok这三个就可以。DevTools 提供了热更新机制,改完代码按一下 Ctrl+F9 就能重启,不用手动停再启;Lombok 能省掉大量 getter/setter 代码,让代码看起来更清爽。后面用到数据库、Redis、消息队列的时候,再往pom.xml里手动加坐标也不迟。

2.2 application.yml 配置文件的结构设计与坑点

默认生成的项目里只有src/main/resources/application.properties,我习惯把它改成application.yml,因为 YAML 格式的缩进结构更直观,写多数据源、多环境配置时清楚得多。

一个最基础的配置长这样:

server: port: 8080 servlet: context-path: /demo spring: application: name: demo-server profiles: active: dev --- spring: config: activate: on-profile: dev server: port: 8081

这个文件里有两个特别值得注意的地方:

第一,context-path一旦设置,所有接口路径都会自动加前缀。比如你写了一个@RequestMapping("/user")的 Controller,实际访问地址就变成http://localhost:8080/demo/user。很多新手配了这个后一直报 404,就是忘了前缀这回事。

第二,spring.profiles.active只是激活某个 profile,但 profile 的定义方式在不同版本里写起来不一样。2.4 之前用的是spring.profiles,2.4 及之后改成了spring.config.activate.on-profile。如果你搜到老教程,直接抄过来会发现配置不生效。这里是很多“版本太高导致问题”的根源之一。

提示:IDEA 里写 YAML 文件时,如果发现spring.application.name这一行完全没有自动提示,多半是缺少Spring Boot插件或者项目没被正确识别为 Spring 项目。可以在项目根目录右键Add Framework Support,勾选 Spring,再重启 IDE 试试看。

3. 核心功能实现与常见注解

3.1 自动装配原理:它凭什么能“自动”?

很多教程会直接告诉你“加一个 starter 依赖就能用了”,但面试或真正遇到问题的时候,往往还是要回到原理。Spring Boot 的核心机制是@SpringBootApplication,它是一个组合注解,由@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个注解组成。

@EnableAutoConfiguration是整个自动装配的开关,它做的事情简单来说是这样的:启动时会去读取所有依赖里的META-INF/spring.factories文件,把这个文件里声明的配置类全部加载进来。但是这些配置类里大量用了@ConditionalOnClass@ConditionalOnMissingBean之类的条件注解,意思是“如果当前 classpath 中存在某个类,才加载这个配置”或者“如果当前 Spring 容器里没有某个 Bean,就自动创建一个默认的”。

举个例子,你引入spring-boot-starter-web后,classpath 里就多了DispatcherServletTomcat等类,自动装配看到这些类存在,就自动配置好内嵌 Tomcat 和 Spring MVC 环境。这就是为什么你不需要写一行 XML 配置就能启动一个 Web 应用。

理解这个原理之后,遇到“为什么这个配置没生效”的问题时,思路会清晰很多:要么是条件注解不满足,要么是自动装配被排除掉了,要么是你自己定义的 Bean 覆盖了默认的。排查方向对了,问题就解决一半。

3.2 核心注解速查:Controller、Service、Repository 层怎么串起来

一个标准的 Spring Boot 接口从入口到落地,一般会经历 Controller → Service → Repository(Mapper)三层。这一小节我整理了几个最常用的注解,新手照着写就能跑通。

注解位置作用
@RestControllerController 类上把类标记为控制器,且方法返回值直接序列化为 JSON
@RequestMapping类或方法上绑定请求路径,也可以限制 GET/POST
@GetMapping方法上简化的 GET 请求映射
@PostMapping方法上简化的 POST 请求映射
@RequestBody方法参数上把请求体里的 JSON 反序列化成 Java 对象
@PathVariable方法参数上从 URL 路径中取值
@RequestParam方法参数上从查询参数或表单中取值
@ServiceService 实现类上标记业务层组件,交给 Spring 管理
@Autowired字段或构造器上自动注入依赖对象

一个小例子:

@RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public Result<User> getUser(@PathVariable Long id) { return Result.success(userService.getById(id)); } }

这里有个细节:@Autowired字段注入虽然写起来方便,但最好不要在业务代码里用,而是用构造器注入。原因是字段注入的依赖是隐式的,单元测试的时候不方便替换 mock 对象,而且极端情况下可能出现循环依赖。用构造器注入,IDEA 也会给出更清晰的依赖提示。

3.3 Banner:一个提升项目辨识度的小功能

Spring Boot 启动的时候会在控制台打印一个 ASCII Art 的 Banner,默认是 Spring 的叶子标志。这个功能虽然不影响业务逻辑,但对内部项目而言,可以用来标注环境信息。

网上有在线 Banner 生成器,输入demo-server之类的文字就能生成 ASCII Art。把生成的字符复制到src/main/resources/banner.txt下,重启项目就会生效。如果想去掉 Banner,配置文件里写spring.main.banner-mode=off即可。

实操心得:Banner 虽然是小事,但在团队协作里有实际价值。我曾经在一个项目里把所有环境的 Banner 都改成一样的,结果测试环境日志跟生产环境长的完全一样,排查问题全靠猜。后来统一规范:dev 环境 Banner 用绿色字符加“DEV”字样,生产环境用红色加“PROD”,一眼就能分辨当前跑在哪个环境。

4. 单元测试与打包部署

4.1 单元测试最佳实战:不只是跑通,而是防回归

Spring Boot 对单元测试的支持非常完善,但它给的是一个骨架,真正的测试策略需要自己设计。我推荐至少写两套测试:

第一套是Web 层冒烟测试。借助@SpringBootTest+MockMvc,把整个 Spring 容器拉起来,模拟 HTTP 请求打到各个接口上,验证状态码和返回结构。这类测试的成本高一点,但胜在“真实”,能发现 URL 路径配置错误、Bean 注入失败这类集成性问题。

第二套是Service 层业务测试。用 Mockito 把 Repository 层 mock 掉,专注验证业务逻辑的取舍。比如一个订单接口,测试“库存不足时抛出异常”“优惠价低于成本时拒绝出单”这类场景。

一个 MockMvc 的示例:

@SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void shouldReturnUserById() throws Exception { mockMvc.perform(get("/api/user/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.name").value("张三")); } }

测试这块有个特别容易被忽略的细节:测试类所在的包路径必须和启动类在同一个包结构下,否则@SpringBootTest扫描不到主类,启动会报“Unable to find main class”。我自己遇到过两次,最后都是把测试类挪到com.example.demoserver对应的路径下解决的。

4.2 使用 Docker 打包并部署到 Docker Desktop

项目写好、测试通过之后,下一步就是部署。我习惯用 Docker 做本机验证,因为 Docker Desktop 提供了一个和生产一致的运行环境,能提前暴露很多环境相关的兼容问题。前提是 Docker Desktop 已经启动,并且 IDEA 的 Docker 插件能正常连上。

先写一个最简单实用的 Dockerfile:

FROM openjdk:8-jdk-alpine VOLUME /tmp COPY target/demo-server-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]

在执行docker build之前,务必先跑一次mvn clean package -Dmaven.test.skip=true把 Jar 包构建出来。这一步容易踩坑的是 Maven 打包时如果本地仓库缺依赖,下载速度会很慢,甚至卡在Downloading状态。解决办法是给 Maven 配一个国内镜像源,在settings.xml里加阿里云镜像。

镜像构建命令:

docker build -t demo-server:1.0 .

运行容器:

docker run -d -p 8080:8080 --name demo-server demo-server:1.0

启动之后打开http://localhost:8080/api/user/1,能正常返回 JSON,说明部署链路完全打通。我这里踩过的坑是:某些 JDK 1.8 镜像的时区是 UTC,会导致日志时间比北京时间晚 8 个小时。解决办法是在 Dockerfile 里加一行:

ENV TZ=Asia/Shanghai

5. 高频问题与排查思路

5.1 application.yml 没自动提示怎么办

这个问题在论坛里被问爆了,尤其是 IDEA 里新建的 Spring Boot 项目,application.yml文件里的 key 全部变成白字,没有任何补全提示。出现这个问题的原因通常是项目里的spring-boot-configuration-processor依赖没加,或者 IDEA 的 Spring 助手插件没启动。

最直接的解法:在pom.xml里添加配置处理器:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>

添加之后再执行一次mvn clean compile,让 IDEA 重新索引。如果还是不提示,试试File → Invalidate Caches / Restart,清缓存重启 IDEA,基本都能解决。

提示:不要在这时候把 IDEA 整个重装,那是用大炮打蚊子,而且不一定有效。

5.2 事务失效场景:为什么数据没回滚

Spring 的事务管理由@Transactional完成,但它有几个失效场景,每个都让人抓狂:

  • 方法内部自调用:同一个类里方法 A 调用方法 B,B 上标了@Transactional,这时事务不生效,因为 Spring AOP 基于代理,自调用绕过代理对象。
  • 方法是privatefinal修饰:代理机制无法切入,事务无效。
  • 异常被捕获:事务方法里 try-catch 把异常吃了,Spring 感知不到,不会回滚。
  • 数据库引擎不支持事务:比如 MySQL 的 MyISAM,默认不开启事务。

我曾在导出报表的功能里栽过跟头:批量插入数据时,有一条数据不合规抛了异常,但因为我在调用点 catch 住了,数据照样写进去了一半。后来把异常重新抛出,并加上rollbackFor = Exception.class才解决。

@Transactional(rollbackFor = Exception.class) public void batchInsert(List<User> users) { for (User user : users) { userMapper.insert(user); } }

5.3 循环依赖:报错信息里全是 Bean

当项目变得庞大、模块划分不清晰时,A Service 依赖 B Service,B Service 又反过来依赖 A Service,Spring 在创建 Bean 的时候就会检测到循环引用,直接抛出BeanCurrentlyInCreationException

Spring Boot 2.6 之前默认允许循环依赖,2.6 开始默认关闭。所以如果你把老项目升级到 2.6 以上,很可能突然出现循环依赖报错。这个问题从根上解决的方法是重构代码,把循环依赖的部分拆开,比如用事件机制或者中间层。

如果实在没时间重构,临时解法是在配置文件里打开spring.main.allow-circular-references=true。但老实说,这种方案只是把问题藏起来,还是建议后续版本里逐步清理。

5.4 外部组件集成时最常见的三个坑

第一,整合 Redis 时连接拒绝。大概率是本地没启动 Redis 服务,或者spring.redis.host写成了localhost但 Redis 只监听了 6379 的 IPv6 地址。检查一下redis-cli ping能否返回 PONG。

第二,整合 MyBatis 时 Mapper XML 找不到。MyBatis 默认扫描classpath*:mapper/**/*.xml,如果 XML 没被编译到 target 里,可以参考pom.xml里加一段资源过滤配置,把src/main/resources下的 XML 文件包含进去。

第三,跨域问题。前端请求都通了,但浏览器报 CORS 错误。最简单的方式是在 Controller 上写@CrossOrigin,但配置多了就很乱。建议全局实现WebMvcConfigurer,统一配置跨域规则。

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE"); } }

6. 个人实操总结与后续学习建议

Spring Boot 这个框架的上手难度远低于当年的 SSH、SSM,但它真正的门槛在细节:版本选型、配置技巧、自动装配原理、事务边界、打包部署。每一样单独看都不难,合在一起就成了一座“新手墙”。

我搭过很多个 Spring Boot 项目,从最初的 1.5 一路用到现在的 3.x,最大的感触是:环境一致性比代码本身更重要。同一个项目,在 A 电脑上能跑,到了 B 电脑上出各种莫名其妙的错,99% 是 JDK/Maven 版本不一致。所以如果你是初学者,建议先把自己机器上的环境固定下来,然后在一个阶段内不要频繁升级。

最后再分享一个我个人的小习惯:每次新建 Spring Boot 项目,我都会在根目录建一个docs/文件夹,把用到的版本号、启动命令、坑点记录进去。项目写了一段时间后回头看看,这份文档的价值远高于代码注释。如果你也想把 Spring Boot 学扎实,建议从现在开始,就像这样一点点积累自己的“踩坑档案”。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询