ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Spring Boot 2.7.18实战:从环境搭建到Docker部署

2026/9/2 10:29:33 拓冰建站 浏览量
Spring Boot 2.7.18实战:从环境搭建到Docker部署 简介一份面向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.x2.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 -version和mvn -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.exampleArtifact项目名建议小写加横杠比如demo-serverType选 Maven 项目Gradle 虽然也不错但国内资料少遇到问题不好搜LanguageJavaPackaging选 Jar绝大多数 Web 项目用 Jar 包部署足够依赖这块刚开始别贪多只勾选Spring Web、Spring Boot DevTools、Lombok这三个就可以。DevTools 提供了热更新机制改完代码按一下 CtrlF9 就能重启不用手动停再启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.profiles2.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 里就多了DispatcherServlet、Tomcat等类自动装配看到这些类存在就自动配置好内嵌 Tomcat 和 Spring MVC 环境。这就是为什么你不需要写一行 XML 配置就能启动一个 Web 应用。理解这个原理之后遇到“为什么这个配置没生效”的问题时思路会清晰很多要么是条件注解不满足要么是自动装配被排除掉了要么是你自己定义的 Bean 覆盖了默认的。排查方向对了问题就解决一半。3.2 核心注解速查Controller、Service、Repository 层怎么串起来一个标准的 Spring Boot 接口从入口到落地一般会经历 Controller → Service → RepositoryMapper三层。这一小节我整理了几个最常用的注解新手照着写就能跑通。注解位置作用RestControllerController 类上把类标记为控制器且方法返回值直接序列化为 JSONRequestMapping类或方法上绑定请求路径也可以限制 GET/POSTGetMapping方法上简化的 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 ResultUser 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-modeoff即可。实操心得Banner 虽然是小事但在团队协作里有实际价值。我曾经在一个项目里把所有环境的 Banner 都改成一样的结果测试环境日志跟生产环境长的完全一样排查问题全靠猜。后来统一规范dev 环境 Banner 用绿色字符加“DEV”字样生产环境用红色加“PROD”一眼就能分辨当前跑在哪个环境。4. 单元测试与打包部署4.1 单元测试最佳实战不只是跑通而是防回归Spring Boot 对单元测试的支持非常完善但它给的是一个骨架真正的测试策略需要自己设计。我推荐至少写两套测试第一套是Web 层冒烟测试。借助SpringBootTestMockMvc把整个 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 插件能正常连上。先写一个最简单实用的 DockerfileFROM 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.skiptrue把 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 TZAsia/Shanghai5. 高频问题与排查思路5.1 application.yml 没自动提示怎么办这个问题在论坛里被问爆了尤其是 IDEA 里新建的 Spring Boot 项目application.yml文件里的 key 全部变成白字没有任何补全提示。出现这个问题的原因通常是项目里的spring-boot-configuration-processor依赖没加或者 IDEA 的 Spring 助手插件没启动。最直接的解法在pom.xml里添加配置处理器dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-configuration-processor/artifactId optionaltrue/optional /dependency添加之后再执行一次mvn clean compile让 IDEA 重新索引。如果还是不提示试试File → Invalidate Caches / Restart清缓存重启 IDEA基本都能解决。提示不要在这时候把 IDEA 整个重装那是用大炮打蚊子而且不一定有效。5.2 事务失效场景为什么数据没回滚Spring 的事务管理由Transactional完成但它有几个失效场景每个都让人抓狂方法内部自调用同一个类里方法 A 调用方法 BB 上标了Transactional这时事务不生效因为 Spring AOP 基于代理自调用绕过代理对象。方法是private或final修饰代理机制无法切入事务无效。异常被捕获事务方法里 try-catch 把异常吃了Spring 感知不到不会回滚。数据库引擎不支持事务比如 MySQL 的 MyISAM默认不开启事务。我曾在导出报表的功能里栽过跟头批量插入数据时有一条数据不合规抛了异常但因为我在调用点 catch 住了数据照样写进去了一半。后来把异常重新抛出并加上rollbackFor Exception.class才解决。Transactional(rollbackFor Exception.class) public void batchInsert(ListUser users) { for (User user : users) { userMapper.insert(user); } }5.3 循环依赖报错信息里全是 Bean当项目变得庞大、模块划分不清晰时A Service 依赖 B ServiceB Service 又反过来依赖 A ServiceSpring 在创建 Bean 的时候就会检测到循环引用直接抛出BeanCurrentlyInCreationException。Spring Boot 2.6 之前默认允许循环依赖2.6 开始默认关闭。所以如果你把老项目升级到 2.6 以上很可能突然出现循环依赖报错。这个问题从根上解决的方法是重构代码把循环依赖的部分拆开比如用事件机制或者中间层。如果实在没时间重构临时解法是在配置文件里打开spring.main.allow-circular-referencestrue。但老实说这种方案只是把问题藏起来还是建议后续版本里逐步清理。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 学扎实建议从现在开始就像这样一点点积累自己的“踩坑档案”。本文还有配套的精品资源点击获取