跟着教程跑通第一条 Demo,看起来是复制粘贴几个命令的事,实际动手往往会栽在各种版本和路径问题上。Demo 项目本身不复杂,复杂的是把环境、依赖、配置、启动顺序全部对齐。很多初学者卡在第一步,不是代码难,而是不知道该从哪个文件开始看,也不知道报错该往哪个方向查。这篇文章会把跑通 Demo 的完整过程拆开讲清楚:拿到项目后先确认什么、按什么顺序操作、每一步如何验证、遇到报错怎么定位,以及跑通之后如何继续往下做。
1. 拿到 Demo 项目后的第一件事,不是运行,而是确认技术栈和运行前提
1.1 Demo 是什么,为什么很多人照着教程也跑不通
Demo 是作者为了让读者快速理解一个库、框架、算法或硬件模块而准备的最小可运行示例。它通常只保留核心链路,去掉日志、权限、监控、多租户等生产环境才需要的东西。正因为紧凑,Demo 对运行环境的要求反而更敏感:作者在自己机器上能跑,换一台机器就可能因为 JDK 版本、Node 版本、Python 版本、系统依赖、端口占用或硬件型号不一致而失败。
最常见的失败原因有四个:
第一,版本漂移。教程写于一年前,当时 Java 8、Spring Boot 2.x 是主流,现在你装了 Java 17 和 Spring Boot 3.x,构造方法、配置项和默认行为都变了。
第二,省略步骤。作者把“安装本地数据库”“设置系统环境变量”“授权脚本执行”“打开系统推送权限”当成默认前提,没有写进正文,导致读者少做了某个关键步骤。
第三,镜像和网络问题。Maven、npm、pip 默认从国外仓库拉依赖,网络不好会在下载阶段卡住,报错却五花八门。
第四,路径和编码问题。项目路径包含中文、空格,或代码文件是 UTF-8、终端是 GBK,都会出现看似“代码有问题”的异常。
所以跑 Demo 的正确姿势应该是先花十分钟做“运行前检查”,而不是急着双击运行。
1.2 先分清技术栈、版本和运行前提
拿到项目压缩包或 Git 仓库后,先不要读代码,先把这些信息整理出来:
| 检查项 | 需要确认的内容 | 从哪里看 |
|---|---|---|
| 开发语言 | Java、Python、Node、C/C++、Kotlin、Go 等 | README、项目后缀 |
| 核心框架或库 | Spring Boot、Vue、React、Flask、Express 等 | pom.xml、package.json、requirements.txt |
| 语言运行时版本 | JDK 8/11/17、Python 3.8/3.10、Node 14/16/18 | README、.java-version、.nvmrc、pom.xml |
| 构建工具 | Maven、Gradle、npm、yarn、pip、CMake | 项目根目录的构建文件 |
| 外部依赖 | MySQL、Redis、Kafka、Nacos、浏览器、Docker | docker-compose.yml、application.yml、README |
| 硬件依赖 | 开发板型号、传感器、显示器、调试器 | 硬件项目的手册、接线说明 |
| 系统要求 | Windows/Linux/macOS,是否区分 32/64 位 | README、驱动安装文档 |
| 网络要求 | 是否需要下载模型、媒体流、远程接口 | 代码中的 URL、API Key 配置 |
这一表整理完,就已经能判断出项目是“纯前端”“前后端分离”“嵌入式烧录”还是“依赖服务器”的类型,后面操作会明确很多。
注意:如果项目没有给出明确版本,落地前一定要先确认依赖版本。不要默认“最新版一定可以”,很多旧 Demo 在设计时根本没考虑过新版语法和 API 的删除。
1.3 用项目结构清单快速核对目录
整理完版本信息后,再快速浏览一遍项目目录。不同语言的项目目录差异很大,但总有几个标志性文件需要看一眼:
# 常见项目根目录结构示例 my-demo/ ├── README.md ├── pom.xml # Maven 项目核心配置 ├── src/main/java/ # Java 源码 ├── src/main/resources/ # 配置文件和静态资源 ├── package.json # Node 项目依赖与脚本 ├── requirements.txt # Python 依赖列表 ├── CMakeLists.txt # C/C++ 构建脚本 └── docker-compose.yml # 容器编排配置这一步的目的是定位三类文件:
- 入口文件:
main方法所在类、index.js、app.py、main.c。 - 配置文件:
application.yml、.env、config.ini、build.gradle。 - 依赖清单:
pom.xml、package.json、requirements.txt、go.mod。
只要这三个文件定位到了,后面查找报错和修改配置就有明确位置,不会在 src 目录里迷路。
2. 按固定顺序拆解 Demo,先看结构再决定怎么跑
2.1 从 README 开始,而不是从代码开始
很多初学者拿到项目后第一反应是打开源码从头读,这是效率最低的方式。源码是作者实现出来的“结果”,而 README 才是作者写给你看的“操作手册”。一份合格的 README 至少应该包含:
- 项目是什么、解决什么问题。
- 环境要求:语言版本、数据库、硬件、系统。
- 安装步骤:依赖安装、编译命令。
- 启动步骤:启动命令、参数。
- 配置说明:环境变量、配置文件、密钥。
- 常见问题:作者踩过的坑。
如果 README 不完整,我建议按这个顺序自己补全信息:先看依赖清单,再搜main方法或入口函数,再看配置文件,最后看测试用例。测试用例往往就是“另一个最小的可运行 Demo”,能帮助理解这个项目应该怎么被调用。
# 伪 README 示例 # Demo 名称:订单状态机 Demo # 环境要求:JDK 11+、Maven 3.6+ # 安装:mvn clean package # 启动:java -jar target/order-state-demo.jar # 配置:修改 src/main/resources/application.yml 中的数据库地址 # 注意:首次启动前需要执行 schema.sql 初始化表结构上面这份 README 已经把关键操作列全了。实际项目中常见问题是 README 没写数据库初始化脚本,导致服务能启动但接口报“表不存在”,这类坑会在后面的排错章节展开。
2.2 梳理项目目录,定位入口文件和配置中心
定位入口文件有一个通用技巧:先找构建配置文件里的启动指令。
举例来说,Maven 项目看pom.xml里的mainClass,Spring Boot 项目找@SpringBootApplication注解所在类;Node 项目看package.json里的scripts.start;Python 项目找app.py、manage.py、main.py;C/C++ 项目找CMakeLists.txt里的add_executable和main函数。
// package.json 中的 scripts 示例 { "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" } }看到scripts.dev是vite,就知道这个前端项目的入口不是某个.html文件,而是需要先执行npm install,再执行npm run dev,然后访问终端输出的本地地址。很多前端 Demo 跑不起来,恰恰是因为用户直接双击了index.html,而 Vite 工程依赖开发和构建机制,不能这样打开运行。
2.3 区分编译型、脚本型、服务型和硬件烧录型 Demo 的运行方式
不同类型的 Demo,运行方式完全不同,提前判断类型可以省掉大量无效操作。
| Demo 类型 | 典型项目 | 运行方式 | 常见验证方法 |
|---|---|---|---|
| 编译型 | Java Maven、C/C++、Rust | 先编译再启动 | 查看编译产物和进程状态 |
| 脚本型 | Python、Node 脚本、Shell | 直接解释执行 | 看终端输出和生成的文件 |
| 服务型 | Spring Boot、Flask、Express | 启动后台服务 | 访问 HTTP 接口或页面 |
| 前端静态型 | Vue、React 构建产物 | 构建后部署到静态服务器 | 浏览器访问页面 |
| 硬件烧录型 | FreeRTOS、裸机工程 | 交叉编译后烧录到开发板 | 观察串口日志和硬件动作 |
| 桌面应用型 | Electron、Qt、C# WinForm | 启动桌面程序 | 观察窗口和交互效果 |
| 驱动安装型 | EtherCAT 驱动、USB 驱动 | 安装内核模块或驱动包 | 用系统命令确认设备状态 |
判断类型的核心依据是“运行后有没有常驻进程”“有没有监听端口”“有没有连接硬件”。服务型 Demo 最常见的坑是没有等待端口就绪就去访问页面,硬件型 Demo 最常见的坑是交叉编译工具链版本不匹配。
3. 跑通一条 Demo 的完整操作流程,每一步都带验证点
3.1 环境准备:系统、语言运行时、IDE 和系统依赖
环境准备的目标不是“装一个能编译的软件”,而是“装一个和 Demo 作者一致的软件”。以 Ubuntu 服务器为例,常用检查命令如下:
# 检查系统版本 cat /etc/os-release # 检查 Java 版本 java -version # 检查 Node 版本 node -v npm -v # 检查 Python 版本 python3 --version pip3 --version # 检查构建工具 mvn -v gradle -v cmake --version如果版本号不满足要求,优先使用版本管理工具安装,而不是直接卸载系统自带的版本。Java 环境可以用sdkman或直接解压 JDK 配置JAVA_HOME,Node 环境可以用nvm,Python 环境建议用pyenv或虚拟环境。
# 以 nvm 安装 Node 16 为例 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash nvm install 16 nvm use 16 node -v这里要注意:很多 Demo 的失败日志并不直接报“Java 版本不对”,而是报UnsupportedClassVersionError、cannot access class或者某种 API 不存在。日志里带有版本字样时,第一优先检查运行时版本。
不要直接这样写:把系统默认的 Python 3.12 卸载后装 3.8。不同系统组件可能依赖默认 Python,直接卸载容易把系统弄坏,正确的做法是用
pyenv或虚拟环境隔离。
3.2 依赖安装:npm、Maven、pip、gradle 的注意事项
依赖安装阶段的主要问题是网络源慢、版本冲突、lock 文件缺失和系统库缺失。不同包管理器的注意事项如下:
| 包管理器 | 典型命令 | 常见问题 | 建议 |
|---|---|---|---|
| npm | npm install | 卡在下载、node-gyp 报错 | 配置国内镜像,安装网络工具依赖前先确认 Python、C++ 编译器 |
| yarn | yarn install | lock 文件版本不一致 | 提交 yarn.lock 到 Git,不要混用 npm 和 yarn |
| pnpm | pnpm install | 软链接导致 node_modules 结构异常 | 优先使用 7.x 以上版本 |
| Maven | mvn clean install | 仓库下载慢、中央仓库缺包 | 配置阿里云镜像,使用-DskipTests |
| Gradle | gradle build | 版本和 JDK 不匹配 | 查看 gradle-wrapper.properties 确认 Gradle 版本 |
| pip | pip install -r requirements.txt | 系统依赖安装失败 | 使用虚拟环境,按错误提示安装apt包 |
以 npm 国内镜像为例,可以这样配置:
npm config set registry https://registry.npmmirror.com npm install使用 Maven 镜像时,在~/.m2/settings.xml中加入镜像配置:
<mirror> <id>aliyun</id> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror>安装依赖后不要急着启动,先用快速命令验证依赖是否完整。前端项目可以检查node_modules是否生成、构建命令能否执行;Java 项目可以直接执行mvn clean package -DskipTests,能出 jar 包说明编译链路是通的。
3.3 修改配置:IP、端口、数据库账号、鉴权信息怎么改
依赖安装完成后,重点检查配置文件。最容易出问题的配置项有:
- 服务端口:默认 8080、3000、5173,被占用时要改成其他端口。
- 数据库地址:本地数据库默认
localhost:3306,远程环境要改成实际 IP。 - Redis 地址:集群模式和单机模式配置格式不同。
- 鉴权信息:API Key、Token、签名密钥,很多 Demo 会使用一个“假密钥”占位。
- 文件路径:Windows 路径和 Linux 路径对分隔符和权限要求不同。
以 Spring Boot 的application.yml为例:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 redis: host: localhost port: 6379配置修改后要验证两个点:一是配置文件的格式是否合法,YAML 对缩进非常敏感,多一个空格或少一个空格会导致配置项失效;二是配置里的地址是否真的可达,可以先用ping、telnet或客户端工具测试。
3.4 启动运行:前台、后台、容器化启动和日志观察
不同 Demo 的启动方式差异很大,但验证逻辑相同:先看进程是否存活,再看日志是否出现关键标志,最后看对外能力是否可用。
# 前台启动 Java 服务 java -jar demo.jar # 后台启动并输出日志到文件 nohup java -jar demo.jar > app.log 2>&1 & # 查看进程 ps -ef | grep demo.jar # 查看端口监听状态 netstat -tlnp | grep 8080前端项目启动后,终端会输出一个本地访问地址,例如http://localhost:5173,这个地址如果无法访问,要先看终端是否有警告信息,再确认防火墙。
容器化 Demo 的启动稍微复杂一点,docker-compose up -d之后要检查容器状态和日志:
docker-compose up -d docker-compose ps docker-compose logs -f --tail=200容器里最常见的坑是容器之间服务名可以通过内网互通,但容器外访问时端口映射没配置对,导致页面能打开、接口连不上。
3.5 验证结果:日志、页面、接口、硬件动作四类验证方式
很多初学者看到终端输出“Started DemoApplication in 2.1 seconds”就认为 Demo 跑通了,这个判断不够完整。按照项目类型,验证链路应该再往后走一步:
| 验证层级 | 操作示例 | 预期结果 |
|---|---|---|
| 进程和日志 | ps -ef、tail -f app.log | 进程存活、日志无 ERROR |
| 页面 | 浏览器访问http://localhost:8080 | 页面出现且无 404 |
| 接口 | curl http://localhost:8080/api/hello | 返回 JSON 或预期文本 |
| 数据库 | 执行查询语句、查看表数据 | 数据新增、状态更新 |
| 硬件动作 | 观察开发板串口输出、LED 变化 | 输出符合预期、硬件响应 |
# 用 curl 验证 Spring Boot 接口 curl -i http://localhost:8080/api/hello # 用 curl 带 JSON 请求体验证 POST 接口 curl -X POST http://localhost:8080/api/order \ -H "Content-Type: application/json" \ -d '{"productId":"1001","count":2}'如果接口返回 500 或 404,说明 Demo 虽然启动了,但某个前置条件没有满足,比如数据库表没初始化、路由路径写错、参数名不一致。此时要回去看日志,而不是反复刷新页面。
4. 运行 Demo 时最常见的五类报错和排查路径
4.1 报错信息定位困难时,先做最小化复现
有些报错日志非常长,混杂着几十行堆栈,初学者容易被干扰。处理原则是先缩小范围:把日志前 5 行和后 5 行摘出来,找到Caused by关键字,它后面往往就是真实原因。
Caused by: java.net.BindException: Address already in use at java.base/sun.nio.ch.Net.bind0(Native Method) at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:xxx)看到Address already in use,就知道是端口被占用,跟业务代码无关。继续排查:
# 查看 8080 端口被哪个进程占用 lsof -i :8080 # 根据 PID 查看进程详情 ps -ef | grep 进程PID定位到占用进程后,要么停掉它,要么修改 Demo 的端口配置。这属于“最小化复现”的案例:与其从头看代码,不如先确认网络资源是否可用。
4.2 端口被占用、地址配置错误和防火墙拦截
这类问题的特征很一致:服务日志显示启动成功,但客户端访问超时或拒绝连接。
| 问题现象 | 常见原因 | 检查命令 | 处理方式 |
|---|---|---|---|
| 服务启动报地址被占用 | 端口被其他进程占用 | lsof -i :8080 | 换端口或停掉旧进程 |
| 本机能访问,远程不能 | 防火墙拦截 | firewall-cmd --list-all | 放行对应端口 |
| 容器内能访问,外部不能 | 端口映射错误 | docker ps、docker-compose ps | 检查 ports 配置 |
| 页面能开,接口 502 | 网关或后端服务未就绪 | ps -ef、netstat -tlnp | 等待后端完全启动 |
如果访问地址写成了localhost而服务监听在0.0.0.0,一部分场景能通,一部分会失败。排查时先确认服务监听地址和客户端访问地址是否一致。
4.3 依赖版本冲突与传递依赖不兼容
Java 项目最常见的依赖冲突是引入了两个不同版本的同名库,Maven 会按“最短路径优先”选一个,但被排除掉的版本可能才是某个功能需要的。看到NoSuchMethodError、ClassNotFoundException或AbstractMethodError时,大概率是依赖版本冲突。
# Maven 查看依赖树 mvn dependency:tree # 定位某个依赖的版本来源 mvn dependency:tree -Dincludes=org.apache.commons:commons-lang3定位冲突后,在pom.xml里显式指定版本号,但不要盲目升级,优先保持 Demo 作者指定的版本组合。如果升级了核心框架,比如从 Spring Boot 2.x 升到 3.x,javax包名需要换成jakarta,这是隐藏的破坏性变更。
Python 项目的处理逻辑类似:pip freeze查看当前环境所有包版本,创建虚拟环境隔离不同项目的依赖。
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt4.4 环境变量和系统级依赖缺失
某些 Demo 会依赖系统环境变量,例如数据库密码通过DB_PASSWORD传入,或者依赖JAVA_HOME、ANDROID_HOME、CMAKE_TOOLCHAIN_FILE等。如果启动日志中出现null、Required environment variable is missing、command not found,就要检查环境变量。
# 检查环境变量是否存在 echo $JAVA_HOME echo $DB_PASSWORD # 查看当前 shell 中所有相关变量 env | grep -E "JAVA|DB_"另一类系统级依赖缺失更难排查,比如 Node 的node-gyp需要 Python 2/3 和 C++ 编译工具,sharp图片处理库需要 libvips,MySQL 客户端库需要libmysqlclient。这类依赖的报错信息通常出现在编译原生模块时,只要看到gyp ERR!、failed to build、curl-config: command not found,就要先安装系统包。
# Ubuntu 安装常见系统依赖示例 sudo apt update sudo apt install build-essential python3 libtool pkg-config4.5 Demo 代码与当前环境不兼容,需要微调源码
当依赖、端口、环境变量都正常,Demo 还是跑不通时,问题可能出在代码本身与当前环境不兼容。常见情况包括:
- 旧 API 在新版本框架中被移除或改名。
- 数据库表结构过时,与实体类不匹配。
- 前端依赖了某个已失效的 CDN 地址。
- 硬编码路径不适用于当前操作系统。
- 学习环境的操作系统或架构与 Demo 设计目标不同。
这类问题可以从三个方向验证:第一,搜索源码中是否有deprecated标记的调用;第二,查看数据库脚本是否需要手动执行;第三,在搜索引擎里搜索报错关键词,看是否是知名框架的版本迁移问题。
如果确认是代码问题,不要担心“改坏 Demo”,项目复制到本地后是独立的,修改源码属于正常操作。但修改前先用 Git 建一个初始提交,方便回滚:
git init git add . git commit -m "backup before modification"5. 以 Android AIDL、WebRTC、FreeRTOS 三类 Demo 为例说明跑通要点
5.1 Android AIDL Demo:SDK 版本、系统服务和跨进程调试
Android AIDL 是 Android 平台上实现跨进程通信的标准方式。跑通这类 Demo 时,最容易出问题的有三个地方:SDK 版本、AIDL 文件编译、真机或模拟器的服务注册。
首先,确认build.gradle里的compileSdk和targetSdk与本地安装的 SDK 版本匹配。如果版本不匹配,Gradle 会报Failed to find target或SDK location not found。
android { compileSdk 34 defaultConfig { applicationId "com.example.aidl.demo" minSdk 24 targetSdk 34 } }其次,AIDL 文件需要放在src/main/aidl目录下,包名要与文件内声明的包名一致。新手经常把 AIDL 文件放在java目录下,导致编译期找不到 binder 接口。
最后,绑定服务的代码要对应“客户端”和“服务端”两个角色。服务端通过onBind返回Stub实现,客户端用bindService获取代理对象。如果客户端连接不上服务端,先检查AndroidManifest.xml中服务是否导出了exported="true",再检查服务端的进程是否存活。
<service android:name=".MyAidlService" android:exported="true" />AIDL Demo 的验证点比较特殊:服务端日志出现onCreate、onBind,客户端日志出现Service connected。只看到 Activity 启动不算跑通,要通过代理对象调用接口方法,并在两端日志中确认参数传递成功。
5.2 WebRTC Demo:浏览器权限、信令服务和媒体设备
WebRTC Demo 通常包含两部分:浏览器端页面和后端信令服务。第一步要确认信令服务能启动,常见技术栈是 Node.js + Socket.IO。浏览器端通过 WebSocket 和后端通信,交换 SDP 和 ICE 候选信息。
跑通 WebRTC Demo 时,以下条件缺一不可:
- 页面必须在
https://或http://localhost下访问,否则浏览器会禁用getUserMedia。 - 摄像头和麦克风授权必须允许,浏览器阻止权限后不会自动弹出。
- 信令服务地址要与前端配置一致,否则双方无法交换 SDP。
- 局域网联调时需要开启 P2P 端口或配置 STUN 服务器,否则只能本地互连。
// 浏览器端获取媒体流的典型代码 navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then((stream) => { const video = document.getElementById('localVideo'); video.srcObject = stream; }) .catch((error) => { console.error('getUserMedia error:', error); });如果getUserMedia报错,按这个顺序排查:
| 报错信息 | 含义 | 处理建议 |
|---|---|---|
NotFoundError | 没有找到摄像头或麦克风 | 插好设备后刷新授权 |
NotAllowedError | 权限被拒绝 | 浏览器设置中重置权限 |
NotReadableError | 设备被其他程序占用 | 关闭占用摄像头的程序 |
OverconstrainedError | 请求分辨率/帧率不满足 | 调整video约束参数 |
WebRTC 是典型的“本地能跑、跨网络很难”的 Demo,跑通本地后不要把成功经验直接复制到公网,公网场景需要信令服务、STUN/TURN 和网络穿透策略配合,属于另一个主题。
5.3 GD32F470 FreeRTOS Demo:交叉编译、驱动安装和硬件烧录
嵌入式硬件 Demo 的跑通逻辑和纯软件项目完全不同。以 GD32F470 微控制器运行 FreeRTOS 为例,流程一般是:
第一步,安装交叉编译工具链。这类工具链是专门为 ARM Cortex-M 内核设计的,系统自带的 gcc 不能用于生成烧录固件。常见工具链是arm-none-eabi-gcc,安装后要检查版本。
arm-none-eabi-gcc --version第二步,确认 Demo 生成的固件格式。FreeRTOS Demo 编译后通常会生成.hex、.bin或.elf文件,GD32 官方烧录工具或 J-Flash 常用.hex或.bin。如果 CMake 或 Makefile 中没有配置输出格式,需要手动添加。
第三步,安装烧录驱动。使用 DAP-Link、J-Link 或板载调试器时,需要安装对应驱动,并在系统中看到调试器设备。设备管理器中如果出现带感叹号的未知设备,说明驱动没装好。
# Linux 下确认 USB 调试器设备是否识别 lsusb dmesg | grep -i usb第四步,烧录并观察串口日志。GD32 的串口通常映射到固定的 USART 引脚,波特率、串口号配置要一致。终端工具用 minicom、picocom 或 Windows 下的串口助手均可:
# Linux 下使用 picocom 打开串口,波特率 115200 picocom -b 115200 /dev/ttyUSB0硬件 Demo 最典型的坑是“编译成功但烧录后运行异常”:原因可能是晶振频率配置、启动文件选择、中断向量表偏移、FreeRTOS 配置中的时钟节拍与芯片主频不匹配。这些参数一旦错误,日志要么完全无输出,要么输出乱码。排查时先确认芯片型号、主频、调试器连接,再检查嵌入式工程配置。
6. 跑通 Demo 之后,怎么把它变成自己的项目
6.1 从“能运行”到“能改造”
跑通 Demo 只是第一步。如果只是让作者的项目在自己电脑上运行一次,学习价值其实有限。真正有价值的是把 Demo 改造成自己的项目:换掉示例数据、改接口路径、替换实现逻辑、增加一个自己的字段或按钮。
建议从小改动开始:
- 给 Demo 增加一个日志输出,观察关键方法的调用顺序。
- 修改一个接口的返回结果,看前端交互是否变化。
- 把硬编码的地址改成从配置文件读取。
- 增加一个异常分支,观察错误处理是否正常。
- 把 Demo 的包名、组件名改成自己的命名前缀。
以 Spring Boot Demo 为例,你可以把示例 Controller 改成自己的业务接口:
@RestController @RequestMapping("/api/user") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/{id}") public Result<User> getUser(@PathVariable Long id) { return Result.success(userService.getById(id)); } }改造过程中,重点关注“改动一处后需要同步改哪些地方”。例如新增一个字段,可能需要改数据库表、实体类、Mapper、DTO、前端表单和校验规则,这个连带修改的链路就是框架设计者希望你理解的依赖关系。
6.2 保留运行记录、最小复现环境和一个验证用例
跑通 Demo 之后,建议保留三类东西,方便后续复盘和排错:
第一,运行记录。把启动命令、配置内容、启动成功日志、接口返回结果保存成文档或 Markdown,下次换机器可以按记录快速恢复。
第二,最小复现环境。如果 Demo 涉及数据库和中间件,可以整理一个docker-compose.yml,把 MySQL、Redis、消息队列等依赖一键启动,这样别人拿到项目时不用逐个安装。
version: "3" services: mysql: image: mysql:8.0 container_name: demo-mysql environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: demo_db ports: - "3306:3306" volumes: - "./sql:/docker-entrypoint-initdb.d"第三,一个验证用例。手动验证太容易遗漏,建议至少有一个脚本或接口用例,能检查“输入什么、处理后返回什么”是否符合预期。可以用 curl 脚本,也可以用 Python 的 requests 或 Java 的 JUnit。
#!/usr/bin/env bash # 简易冒烟测试脚本 BASE_URL="http://localhost:8080" echo "check health:" curl -i "$BASE_URL/actuator/health" || exit 1 echo "" echo "check api:" curl -i "$BASE_URL/api/hello" || exit 16.3 面向生产环境补齐日志、监控、异常处理和回滚方案
学习环境只要能跑通,生产环境则还需要补齐一套工程能力。即使你只是拿着 Demo 做二次开发,也要从一开始就清楚生产环境需要额外注意什么:
| 关注点 | 学习环境常见状态 | 生产环境需要补充 |
|---|---|---|
| 配置 | 写死在 application.yml | 配置外置化,敏感信息走密钥管理 |
| 日志 | 输出到终端 | 按天滚动、归档、日志采集 |
| 数据库 | 使用 root 和简单密码 | 最小权限账号、连接池参数调优 |
| 异常处理 | 抛异常后退出 | 统一异常处理、熔断、降级 |
| 监控 | 没有监控 | 健康检查、指标采集、告警规则 |
| 回滚 | 改错就重装环境 | 版本备份、备份数据库、灰度发布 |
在这个阶段,要把 Demo 里为了简洁而省略的部分补回来。比如不在 Controller 里写大量业务逻辑,而是拆成 Service 和 Repository;不在代码里写死连接串,而是使用环境变量;不忽略异常堆栈,而是记录到日志并通过监控报警。
7. 通用排查清单:每次跑 Demo 前先过一遍
7.1 环境检查清单
下表适合打印出来贴在工位上,每次拿到新 Demo 都过一遍:
| 序号 | 检查项 | 检查命令或方式 | 通过标准 |
|---|---|---|---|
| 1 | 操作系统版本 | cat /etc/os-release | 与项目要求一致 |
| 2 | 语言运行时版本 | java -version、node -v、python3 --version | 满足 README 范围 |
| 3 | 构建工具版本 | mvn -v、gradle -v、npm -v | 能正常输出版本号 |
| 4 | 依赖镜像 | npm config get registry、检查 settings.xml | 下载速度正常 |
| 5 | 数据库和中间件 | mysql --version、redis-cli ping | 返回 PONG 或正常连接 |
| 6 | 端口占用 | lsof -i :8080 | 没有冲突进程 |
| 7 | 环境变量 | echo $JAVA_HOME、echo $ANDROID_HOME | 路径存在且正确 |
| 8 | 硬件设备 | lsusb、设备管理器 | 设备被系统识别 |
| 9 | 配置文件 | 检查 application.yml、.env | 地址、账号、端口正确 |
| 10 | 初始化脚本 | 查找 schema.sql、init.sql | 已按说明执行 |
7.2 启动前和启动后的快速排错顺序
当运行出现异常时,按下面的顺序排查,比直接查源码高效得多:
- 确认当前工作目录是否正确,很多人是在项目根目录的上一层执行了命令。
- 确认命令是否完整,例如 Java 项目执行
java -jar前是否先执行了mvn package。 - 确认配置文件是否被正确识别,YAML 缩进和属性名是最容易被忽略的问题。
- 确认服务日志中最后一次
ERROR或Caused by的位置。 - 确认端口、数据库、Redis 等外部资源是否可用。
- 确认系统防火墙、云安全组是否放行对应端口。
- 确认依赖版本之间是否兼容,必要时查看依赖树。
- 确认是否有初始化数据要执行,例如建表、造数据、生成配置。
- 如果本地原来跑过一个旧项目,确认是否残留旧进程或旧配置。
- 确认硬件项目中的烧录工具、串口号、波特率和驱动是否全部就绪。
7.3 长期有效的运行和学习建议
跑 Demo 是一个高频动作,学会方法比这次成功更值钱。建议养成三个习惯:
第一,每次跑新项目都独立创建一个虚拟环境或用容器隔离依赖,避免全局环境越来越乱。Python 用venv,Node 用本地node_modules和明确的package.json,服务依赖用 Docker Compose。
第二,遇到报错先记录,再查资料。把完整报错日志保存下来,搜索时优先用“英文报错核心词 + 项目框架版本”,比如Spring Boot 3.2 NoSuchMethodError,这样定位比直接搜中文描述更快。不要只复制“为什么报错”这种模糊说法。
第三,跑通后花十分钟复盘:这个 Demo 的入口在哪、配置文件有哪些、核心调用链是什么、哪些环节去掉也能跑、哪些环节必须严格依赖。复盘一段时间后,你拿到新 Demo 的判断速度会明显加快,不再需要从头摸索。
跑通第一条 Demo 的价值不在“能运行”,而在于建立了完整的运行认知:从环境、依赖、配置、启动、验证到排错,每个环节都有对应的检查点。后续学任何新框架、新库、新硬件,都可以复用这套方法。把流程固定下来,跑 Demo 就从“碰运气”变成“照单执行”。