资讯动态

无需IDE,手动创建Spring Boot工程的完整步骤与踩坑记录

发布时间:2026/10/15 11:37:10 来源:尧图企业网站定制
有一次出差到某客户现场临时接到一个接口联调需求需要在一台只有命令行环境的机器上起一个 Spring Boot 服务。那会儿我才发现自己平时写 Spring Boot 已经离不开 IDEA 的插件了从选依赖到生成工程全是在图形界面里点出来的。对面同事问“能不能不用 IDE 直接建一个工程”我硬着头皮用 Maven 一点一点把目录结构、pom.xml、启动类搭了出来。最后执行mvn spring-boot:run看到 Tomcat 端口起来的那一瞬间我才真正想明白IDEA 插件只是替你完成了一套固定的文件生成动作手动创建 Spring Boot 工程这件事本身没有任何魔法。这篇文章就是把那一次手动搭建的过程整理成一份可以直接照着操作的指南。不管你是刚学 Spring Boot 想搞懂底层骨架还是需要在 CI/CD 脚本、远程服务器上不依赖 IDE 初始化项目这套方法都能直接落地。我会把每一步的“为什么要这样做”也讲清楚而不是只丢命令和代码。1. 为什么非要手动创建先想清楚这层关系1.1 插件帮你做了什么手动创建又在做什么IDEA 里的 Spring Initializr 插件说白了就是一个模板生成器。它背后调用的是一个初始化服务根据你在界面上勾选的依赖生成一个压缩包里面包含工程结构、Maven 坐标、启动类、配置文件。这个过程很快但也容易让人产生一个错觉Spring Boot 工程是 IDE 帮你“变”出来的。实际上一个最简 Spring Boot 工程的硬性组成部分只有三块Maven 配置文件 pom.xml、启动类带 main 方法的类、以及一个资源目录。剩下的 Controller、Service、配置文件都是在这个最少结构之上生长的。手动创建就是把这三块内容用自己的命令和编辑器搭出来把插件的黑盒拆开。手动创建还有一个额外收获你会知道哪些文件是 Spring Boot 框架真正需要的哪些只是 IDE 审美下的“周边产品”。比如说.mvn/maven.config、.gitignore这些文件对工程启动没有直接影响只是工程型社区实践里的常备选项。理解了这一点之后你再去看别人生成的工程时就不会被一堆未知文件搞得一头雾水。1.2 什么场景下“无插件”反而是优势依赖插件久了很容易忽略它在特定场景下的短板。我在实际工作中遇到过三类场景手动创建几乎是唯一选择第一类是受限环境。有些客户内网部署机禁止安装图形界面软件或者出于安全合规原因不允许随意安装 IDE你只有终端和命令行。这种情况下手动搭建是基本功。第二类是自动化构建链路。CI/CD 流水线里不可能有“人工点一下 IDEA 插件”这个环节。初始化工程、生成骨架这些动作都要能在脚本里完成。你哪怕用 curl 去调初始化服务最终落地方式还是命令行手工生成文件或者直接用 Maven 项目模板。第三类是理解底层逻辑。如果你想过搞清楚 Maven 坐标、组件扫描、内嵌 Tomcat 这些概念到底怎么协同工作手动创建一次比看十遍文档都管用。插件隐藏了太多细节而这些细节恰恰是排查生产问题时的救命稻草。当然也不是所有情况都推荐手动。你要是只学习一个 DemoIDEA 插件的确高效但如果你希望“心里有数”那手动创建这条路早晚得走一次。2. 开工前检查环境三行命令确认 JDK 和 Maven2.1 JDK 与 Maven 的版本选择动手之前先把环境捋清楚。手动创建 Spring Boot 工程虽然不需要 IDE但操作系统里必须提前装好 JDK 和 Maven环境变量也要配好。JDK 版本怎么选我建议看你想用哪个 Spring Boot 大版本。Spring Boot 2.x 官方要求 JDK 8 起步主流生产环境用 JDK 8 或 JDK 11 都很稳。Spring Boot 3.x 则强制要求 JDK 17 及以上因为它整体迁移到了 Jakarta EE很多底层 API 都换掉了。这个对应关系不是随便说说的版本选错了后面编译直接失败ClassNotFoundException 和 UnsupportedClassVersionError 都会冒出来。Maven 版本至少要 3.6 以上。过老的 Maven 在解析 Spring Boot 相关依赖时偶尔会出莫名的编译问题。打开终端依次执行java -version mvn -v看到类似java version 1.8.0_202或openjdk 17.0.2字样说明 JDK 没问题。Maven 输出里有Apache Maven 3.8.x并且对应Java version一行的结果和java -version一致就说明 Maven 能用。这里需要特别确认的一点是mvn -v里显示的 Java 版本必须和编译目标一致因为 Maven 运行时用的 JDK 和JAVA_HOME指向同一套环境。如果mvn -v输出的是 JDK 8 而你打算在 Spring Boot 3 上用 JDK 17那就得先去改JAVA_HOME环境变量。2.2 配置 Maven 国内镜像避免依赖下载等到地老天荒手动创建工程没有 IDE 帮你悄悄配好镜像这一步如果漏了你会在执行mvn compile的时候感受到什么叫绝望。Maven 默认从中央仓库下载依赖网络一旦不稳可能一个spring-boot-starter-web都要下载好几分钟甚至到一半直接失败。解决办法是修改 Maven 全局配置文件settings.xml位置一般在 Maven 安装目录下的conf/settings.xml。在mirrors标签里加上一个镜像配置:mirror idaliyunmaven/id mirrorOfcentral/mirrorOf nameAliyun Maven Mirror/name urlhttps://maven.aliyun.com/repository/central/url /mirror这个配置的含义是所有对中央仓库的下载请求都会转发到上面这个地址。mirrorOf填central表示只对中央仓库生效不影响你后续在 pom.xml 里配置其他仓库源。配完之后随便找一个工程执行mvn help:effective-settings如果输出里能看到这个镜像说明配置生效了。这里有个我踩过的坑改全局 settings.xml 之后某些公司内部私服场景反而会出问题。如果你们公司有自己的 Maven 私服镜像地址应该配成私服地址而不是一味用公共镜像。手动创建的“手动”之处就在于每一个环节你都得知道自己在配什么。3. 手写 pom.xml整个工程的地基别抄错3.1 parent 与 starter 的搭配逻辑pom.xml 是 Maven 工程的心脏。新手最容易犯的错误是复制一段网上代码就开始改改完也不知道里面几个大标签分别是干什么的。核心先说两件事parent 和 starter。Spring Boot 工程里的parent标签大多数时候指向spring-boot-starter-parent。这个东西真正的价值是版本管理。它内部定义了一套dependencyManagement把 Spring Boot 各个组件、常用第三方库的版本全部固定住了。只要继承它你在dependencies里写依赖时就不需要写version。如果不继承 parent你就得在 dependencies 里给每个依赖手动写版本号而且还得保证版本之间互相兼容。手动创建工程时很多人觉得这个版本依赖关系太绕宁可全部写全版本号也不想引入 parent。这样做短期能跑后期升级依赖时就会变成一场灾难因为你根本不知道哪个版本和哪个版本能配合。starter 呢是一个“场景化依赖”。以spring-boot-starter-web为例它本身只依赖一个 pom但通过传递性依赖把 Web 开发常用的一堆 jar 全部引入了包括内嵌 Tomcat、Spring MVC、Jackson 等。这就是为什么你加了 starter-web 之后没做任何额外配置就能直接启动一个带 HTTP 端口的服务。所以手动创建时请记住parent 用来锁版本starter 用来引依赖两个配合使用你的 pom.xml 可以保持很短同时不会产生版本冲突。3.2 最小可用 pom.xml 完整示例下面这个是我手写工程时最常用的最小配置你可以直接抄但建议每行都过一遍?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent groupIdcom.example/groupId artifactIdmanual-boot-demo/artifactId version1.0.0-SNAPSHOT/version namemanual-boot-demo/name description手把手手动创建的 Spring Boot 工程/description properties java.version1.8/java.version /properties 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 /project需要解释两个容易忽略的点。第一个是relativePath/这个空标签。它的作用是告诉 Maven父 pom 从仓库里解析不要从本地相对路径里找。很多教程会省掉这行导致在本地目录结构不规则时 Maven 试图去上级目录找父 pom报出奇奇怪怪的错误。第二个是spring-boot-maven-plugin。没有它你用mvn package打出来的 jar 只是一个普通 jar执行java -jar会提示“没有主清单属性”因为默认打包方式不会把 Class-Path 和 Main-Class 写进 manifest。加上这个插件后Maven 在 package 阶段会额外执行 repackage 操作把应用所有依赖塞进同一个包并生成可执行的“fat jar”。这也是后面“一键启动”的关键前提。3.3 版本号到底怎么选版本选择这事手动创建时特别容易被忽略。我见过不少人在 pom.xml 里复制了一个高版本号但本机 JDK 是 8结果一跑就报UnsupportedClassVersionError。这里给出一个实际可用的对照思路Spring Boot 版本最低 JDK推荐 JDK说明2.7.x88/112.x 系列最后的维护版本生产环境最稳妥3.0.x1717迁移到 Jakarta EE旧代码需要改动3.2.x1717/21较新的稳定版本适合新项目选型如果你正在学 Spring Boot我建议先用 2.7.x因为网上绝大多数资料、第三方组件适配度都基于 2.x遇到问题更好查。如果你的项目是全新的、且团队 JDK 已经升级到了 17 以上那就直接用 3.2.x别再用旧版本给自己埋坑。另一个细节是java.version标签。它控制的是源码编译级别要与本机 JDK 和 Spring Boot 版本一起考虑。比如你本机是 JDK 17但 pom 里java.version1.8/java.version那编译时会强制用 release 8 的行为去编译某些用了新 API 的第三方库反而会编译报错。手动创建这个标签是你自己写的别照抄别人工程里的值。4. 创建启动类和基础配置决定“一键启动”成败的两块拼图4.1 启动类代码与包路径的讲究手动创建工程时目录结构必须自己建立。你可以在终端里依次执行下面几条命令mkdir -p src/main/java/com/example/demo mkdir -p src/main/resources mkdir -p src/test/javasrc/main/java是 Java 代码根目录src/main/resources放配置文件和静态资源src/test/java放测试代码。这是 Maven 的约定目录结构不用额外配置Maven 会自动识别。 在这些手动建立的目录下创建一个启动类文件DemoApplication.java代码如下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); } }代码本身并不复杂但要注意包路径的问题这是我手动创建过程中最容易踩坑的地方。SpringBootApplication注解内部包含了ComponentScan扫描的默认范围是当前类所在包及其子包。也就是说如果启动类在com.example.demo包下Controller、Service 只有放在com.example.demo.controller、com.example.demo.service这类子包里才能被自动扫描到。如果你把启动类随便放在某个夹层包里比如demo.DemoApplication而 Controller 放在com.example.demo.controller那启动后 Controller 不会被注册接口 404 没任何报错排查起来相当隐蔽。解决办法其实很简单新建工程时先把包路径设计清楚或者在使用SpringBootApplication时给scanBasePackages参数指定扫描范围。4.2 application.properties 里先安置这几个基础参数工程刚创建时没有配置文件应用也能启动但最好还是先加入src/main/resources/application.properties。这文件名字是固定的Spring Boot 启动时会自动读取。我建议的最小配置长这样spring.application.namemanual-boot-demo server.port8081spring.application.name用来设置应用名日志里会显示这个前缀多个应用调试时一眼就能区分。server.port是 HTTP 服务端口默认是 8080。我故意改成了 8081因为本地机器经常会有别的服务占着 8080手动创建工程时直接换个端口能少很多麻烦。如果你以后要用配置中心、注册中心这些组件spring.application.name是它们识别服务身份的关键现在养成写它的习惯没有坏处。相比 YAML 格式properties 格式对新手更友好没有缩进问题手动创建阶段完全够用。等工程复杂起来了再换成 YAML结构更清晰。4.3 顺手加一个 Controller 验证接口能否命中为了验证手动搭建的工程真的能跑通我建议再加一个最简单的 Controller。在com.example.demo.controller包下创建HelloController.javapackage 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, Manual Spring Boot!; } }RestController会把返回值直接写进 HTTP 响应体GetMapping(/hello)把/hello路径映射到这个方法上。加了这一层之后启动完事就能立刻通过接口验证整个链路是否正常不用去看那些包扫描日志逐行分析。到了这一步手动工程的骨架已经齐了一个 pom.xml、一个启动类、一个配置文件、一个验证接口。接下来就是见证“一键启动”的时刻。5. 在命令行实现一键启动两种启动姿势实测对比5.1 开发模式mvn spring-boot:run命令行进入工程根目录执行mvn spring-boot:run这是开发阶段最常用的启动方式。它做的事情很简单Maven 先解析 pom.xml 下载缺失依赖编译代码然后调用 Spring Boot 插件启动应用。第一次执行时依赖下载会慢一些耐心等待即可。看到日志里这样一行就说明启动成功Tomcat started on port(s): 8081 (http)然后应用继续走初始化流程到Started DemoApplication in X.XX seconds时整个应用才算完全就绪。此时别急着庆祝先在另一个终端窗口验证接口curl http://localhost:8081/hello只要输出Hello, Manual Spring Boot!整个手动链路就彻底跑通了。这里有一个很实用的经验mvn spring-boot:run是直接占用当前终端的CtrlC 可以停止。如果你不想让日志刷屏可以加-q参数减少日志输出或者重定向到日志文件mvn spring-boot:run app.log 21这样所有输出都会进app.log配合tail -f app.log查看终端会清爽很多。5.2 部署模式打包成可执行 jar 再运行开发模式适合本地调试但你要是部署到测试环境或者想做一个更接近线上运行的方式那就得打包。在工程根目录执行mvn clean package -DskipTestsclean会删除上次编译生成的 target 目录package执行打包-DskipTests跳过测试。执行完成后在target目录下找到manual-boot-demo-1.0.0-SNAPSHOT.jar。运行它就一条命令java -jar target/manual-boot-demo-1.0.0-SNAPSHOT.jar你会看到和mvn spring-boot:run几乎一样的启动日志。不同的是这次运行的是独立 jar不依赖 Maven只要有 JDK 环境就能跑。如果在执行java -jar时出现no main manifest attribute的报错基本确定是 pom.xml 里漏了spring-boot-maven-plugin。这是我在手动创建工程时见过最多的问题。加了插件之后mvn package生成的 jar 是 fat jar而插件缺失时生成的只是普通 jar内部没有启动入口信息。5.3 给 Linux 环境配一个 start.sh 启动脚本既然都手动创建了干脆把启动脚本也配好这才算真正的“一键启动”。我平时在 Linux 服务器上最常用的脚本模板如下#!/bin/bash APP_NAMEmanual-boot-demo JAR_FILEtarget/manual-boot-demo-1.0.0-SNAPSHOT.jar PID$(ps -ef | grep $APP_NAME | grep -v grep | awk {print $2}) if [ -n $PID ]; then echo Stopping existing app: $PID kill $PID sleep 3 fi echo Starting $APP_NAME ... nohup java -jar $JAR_FILE --server.port8081 app.log 21 echo App started, logs at app.log执行前记得给脚本加上执行权限chmod x start.sh ./start.sh脚本里的第一段是查找已有 Java 进程并杀掉防止端口冲突。nohup加让应用在后台运行退出终端也不会中断。 app.log 21把标准输出和错误日志都写进 app.log。后续排查问题时就靠这个文件。6. 从零到一踩坑实录常见问题排查与解决6.1 依赖下载慢或者直接失败手动创建工程最直观的痛点是依赖下载。可能在执行mvn spring-boot:run时Maven 卡在下载spring-core或者spring-webmvc这些大 jar 上很久不动。这个问题我在前面提过核心解法是配置镜像。但还有一个隐藏问题Maven 本地仓库.m2/repository里如果之前下载过损坏的 jar后续构建会一直报Could not resolve dependencies。这种情况下删掉对应目录重新下载就好比如rm -rf ~/.m2/repository/org/springframework然后重新执行 Maven 命令。别一股脑把整个.m2/repository都删了除非你是刚开始用 Maven否则全部重下很浪费时间。6.2 端口被占用刚启动就报错启动时如果日志里出现Web server failed to start. Port 8081 was already in use.说明端口被别的进程占了。排查命令很简单lsof -i:8081输出的 PID 就是占用进程的标识确认无误后kill -9 PID强杀。如果这是生产环境谨慎操作最好先确认进程是什么服务。另一个办法是只把当前应用的端口改掉启动参数里追加java -jar target/manual-boot-demo-1.0.0-SNAPSHOT.jar --server.port8082在 Spring Boot 里命令行参数中的内嵌配置优先级很高足以覆盖 application.properties 里写的值。这是临时起服务时最灵活的端口切换方式。6.3 页面 404Whitelabel Error Page 的三种可能手动创建工程后启动没报错但访问/hello时返回一个带大标题 whitelabel 的 404 页面。这里的坑在于应用没有崩溃但也没有找到能处理请求的 Controller。按我的排查顺序三步定位第一步确认请求路径是否拼错。GetMapping(/hello)访问的 URL 是/hello不是/hello/也不是/hello.do。如果检查无误继续下一步。第二步确认启动类与 Controller 的包关系。启动类在com.example.demo下Controller 必须在它的子包中不然组件扫描扫不到。直接看启动日志里有没有输出Mapped {[/hello]} onto public java.lang.String ...如果没看到这行说明 Controller 压根没被注册多半是包路径问题。第三步确认 Controller 是否标注了正确的注解。RestController和Controller的区别要分清楚只有Controller的话方法返回值会被当作视图名处理而不是直接响应文本除非在方法上再加ResponseBody。综合列一个速查表现象可能原因解决方向访问路径 404路径拼错 / 没加映射注解检查GetMapping与 URL启动日志无接口映射Controller 不在扫描子包调整包路径返回 500 或页面模板错误Controller返回值当视图处理使用RestController或加ResponseBody6.4 JDK 版本与 Spring Boot 版本不匹配这类错误通常出现在编译阶段或启动早期。如果你用 JDK 8 去编译 Spring Boot 3.x 的工程会看到类似UnsupportedClassVersionError: org/springframework/boot/... has been compiled by a more recent version of the Java Runtime原因很简单Spring Boot 3.x 的 class 文件版本高于 JDK 8 能识别的最髙版本。解决方式也直接要么本机装一个匹配的 JDK 并切换JAVA_HOME要么把 Spring Boot 版本降到 2.7.x。还有一个相似的坑是编译级别问题。pom.xml 里java.version写的值和本机 JDK 不一致时Maven 会用${java.version}去配置编译器插件导致编译报错invalid target release: 17这种情况我建议直接把 pom.xml 里的java.version改成 8 或 17要和java -version显示的主版本对齐别存侥幸心理。7. 进阶玩法把“手动创建”变成你的长期工作流7.1 常用 Maven 命令组合手动创建工程之后命令行操作你绕不开我把几个高频 Maven 命令整理成一套可复用的组合用法mvn clean package -DskipTests这个命令组合是我最常用的。clean清掉 targetpackage打包-DskipTests跳过测试适合快速出部署包。注意-DskipTests只是不执行测试但会编译测试代码如果你想连编译都跳过用-Dmaven.test.skiptrue。mvn spring-boot:run -Dspring-boot.run.profilesdev这条命令用于指定启动的环境前提是 application.properties 里配过spring.profiles.active或者你在 resources 目录下放了application-dev.properties这类环境文件。mvn dependency:tree这个命令能打印出整个工程的依赖树排查传递依赖冲突时特别好用。手动创建的工程没有 IDE 自带的可视化依赖图多依赖这个命令会让你排查效率高很多。7.2 多环境配置与启动参数手动创建工程后我建议立刻建立一套环境配置文件习惯。在src/main/resources下创建application.properties application-dev.properties application-prod.properties基础配置留在 application.properties 里各环境差异写在对应文件里。比如开发环境端口 8081生产环境端口 80只需要在各自文件里写server.portxx即可。启动时通过参数指定java -jar target/manual-boot-demo.jar --spring.profiles.activeprod这个参数完全可以写进前面提到的start.sh脚本里只需要在java -jar后面追加即可。手动创建工程带来的一个好处是你会更自然地理解配置优先级命令行参数 环境变量 application-{profile}.properties application.properties。这个顺序在排查“为什么我的端口没生效”时非常管用。7.3 不装 IDE 的日常调试思路没有 IDE 的情况下的常规调试思路主要靠日志和外部请求验证。启动时在命令行加--debug可以开启 Spring Boot 的调试日志输出帮你看到自动配置的决策细节。访问接口用curl -v http://localhost:8081/hello-v参数可以看到完整的 HTTP 请求响应头特别适合确认服务器是否真的处理了请求。查进程和端口用ps -ef | grep java、lsof -i:端口这两套组合就足够覆盖大多数问题。如果你需要更深入的在线调试可以考虑用诊断工具和服务接口比如 Spring Boot Actuator。手动创建工程的 pom.xml 里引入spring-boot-starter-actuator后启动应用通过 HTTP 访问一些内置端点可以拿到健康状态、线程快照、配置明细等运行时信息。这样即使完全没有 IDE也能有可量化的手段去判断应用状态是否正常。说到我自己现在新建 Spring Boot 工程反而不太喜欢用 IDEA 插件直接生成了。倒不是插件不好而是手动创建过一次之后整个工程骨架在我脑子里非常清晰遇到环境受限、构建脚本里要初始化工程、或者半夜排查依赖问题时那种“心里有数”的底气是插件给不了的。如果你也想把这份底气攒起来我建议你从手边下一个工程开始别开插件照着这篇文章手动搭一遍哪怕只是原样敲一遍理解程度也比一路 Next 高出一个档次。等你真的手动创建过一次再回头看 IDEA 前几天帮你生成的工程你会发现原来 Spring Boot 的起点一直都在你的掌控之中。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑