CH 00环境准备
工欲善其事,必先利其器。本章不写一行 AI 代码,但决定你后面十一章是「一路顺畅」还是「天天踩坑」。跟着本章把 JDK、IDEA、Maven、API 密钥四件事全部就位,并逐项验收。
教学设计说明(教师参考):本章建议以「课前自学 + 课上抽查」方式实施——环境安装视频化或文档化发给学生课前完成,课上用 s9 的验收清单逐项检查(10 分钟内完成)。重点盯两件事:① mvn -v 的输出(JDK 与 Maven 版本是否匹配);② 密钥是否按要求放进环境变量而不是源码。本章验收不通过的学生,不允许进入第 1 章实操。
0.1版本基准:本课程所有代码基于这套环境
先记住这张表。全课程的每一行代码、每一条命令,都以它为基准。版本不一致是学生环境问题里占比最高的一类——遇到报错先回来对照这张表,再动手排查。
| 组件 | 版本 | 说明 |
|---|---|---|
| Spring AI | 2.0.1 | 由 spring-ai-bom 统一管版本;2.0 与 1.x 的 API 有大量不兼容差异,网上教程混着看时会踩坑 |
| Spring Boot | 4.1.x | 课程示例用 4.1.1;Boot 3.x 与本课程不兼容,不可混用 |
| Java(JDK) | 17 | Spring AI 2.0 的硬性底线是 17;装 21 也可以,但别装 8/11 |
| Maven | ≥ 3.6.3(推荐 3.9.x) | Boot 4 的插件要求 3.6.3 以上;老版本 3.6.1 会直接构建失败 |
| IDE | IntelliJ IDEA | 社区版(Community)完全够用,无需旗舰版 |
| 应用端口 | 8080 | 全部示例统一 8080,与课程工程配置一致 |
| 模型服务 | DeepSeek(默认) | OpenAI 兼容协议;换其他厂商见第 6 章「多模型」 |
spring-ai-bom 版本号——2.x 开头才是本课程兼容的写法。这不是教程写错了,是 API 换代了。
0.2软件清单与检查命令
| 要装什么 | 去哪下载 | 装完怎么验证(命令行执行) |
|---|---|---|
| JDK 17 | Oracle 官网 / Adoptium(temurin) | java -version 输出包含 17 |
| IntelliJ IDEA 社区版 | jetbrains.com | 能正常启动并打开一个项目 |
| Maven 3.9.x | maven.apache.org(IDEA 内置版也可用) | mvn -v 输出 Maven 与 Java 版本 |
| API 测试工具 | curl(系统自带)/ Apifox(推荐图形界面) | curl --version;或 Apifox 能打开 |
/path/to 这种写法,把它换成你自己的实际路径即可。
★ 先看这里:三种终端的命令差异(照抄命令前必读)
后面每一章都会给可以直接复制的命令。但同一句命令在三种终端里不一定都能跑——这是学生"照抄了却报错"的第一大来源。花 3 分钟看完这张表,能省掉后面几十次无效排查。
| 差异点 | Git Bash 本文示例默认 | CMD(命令提示符) | PowerShell |
|---|---|---|---|
| JSON 里的双引号 | 直接照抄:-d '{"question":"hi"}' |
外层用双引号、内部转义:-d "{\"question\":\"hi\"}" |
会被悄悄吃掉:内嵌双引号被剥离,服务端收到 {question:hi} → 报 400。见下方「三条出路」 |
curl 这个名字 |
真实的 curl | 真实的 curl | curl 是 Invoke-WebRequest 的别名,参数写法完全不同 → 必须写 curl.exe |
Maven 的 -D 参数 |
mvn dependency:tree -Dincludes=org.springframework.ai |
同上,可直接用 | -D 传参会被拆坏 → 改用 --define "includes=org.springframework.ai" |
| 读环境变量 | echo $DEEPSEEK_API_KEY |
echo %DEEPSEEK_API_KEY% |
echo $env:DEEPSEEK_API_KEY |
| 命令换行续行符 | 反斜杠 \ |
脱字符 ^ |
反引号 ` |
| 脚本文件后缀 | .sh |
.bat |
.ps1(执行需 -ExecutionPolicy Bypass) |
- 换 Apifox(图形化接口工具):URL、Header、Body 分开填,不用跟引号搏斗。初学者最省心,课程后续联调也建议用它。
- 改用 PowerShell 原生命令
Invoke-RestMethod(它不认识-d,但能正确解析 JSON):Invoke-RestMethod -Uri http://localhost:8080/api/v1/chat/ask ` -Method Post -ContentType "application/json" ` -Body '{"question":"用一句话解释 Spring AI"}' - 换成 Git Bash 或 CMD 再跑 curl:Git Bash 可直接照抄本文示例;CMD 按上表把内层双引号写成
\"。
-o /dev/null(丢弃输出)在 Windows 上会写入失败并报错,看起来像"请求失败",其实请求是成功的。想只看状态码,用 curl -i -s ...,或者在 Git Bash 里用 -o /dev/null。
把重复命令固化成一键脚本(强烈建议)
环境准备好之后,你会反复执行三类命令:清端口 → 启动应用 → 打接口验证。每次手敲既慢又容易错,建议在工程根目录存成三个脚本(本课程配套 lab 已自带,可直接用):
① stop-app.ps1:解决「Port 8080 was already in use」(写成文件执行,避免长命令在终端里被引号拆坏):
# stop-app.ps1 —— 找到并结束占用 8080 的进程
$pids = (netstat -ano | Select-String ":8080\s+.*LISTENING") -replace '.*\s+(\d+)$', '$1' | Sort-Object -Unique
if (-not $pids) { Write-Host "8080 未被占用"; exit }
foreach ($p in $pids) {
Write-Host "结束进程 PID=$p"
Stop-Process -Id $p -Force -ErrorAction SilentlyContinue
}
Write-Host "完成"
② start-app.ps1:把密钥注入本次会话再启动(避免"设了环境变量但进程读不到"):
# start-app.ps1 —— 从用户级环境变量取密钥,注入当前会话后启动
$env:DEEPSEEK_API_KEY = [Environment]::GetEnvironmentVariable("DEEPSEEK_API_KEY", "User")
if (-not $env:DEEPSEEK_API_KEY) { Write-Host "找不到 DEEPSEEK_API_KEY,请先按 0.6 节设置"; exit 1 }
Set-Location "D:\path\to\lab" # ★ 换成你自己的 pom.xml 所在目录
.\mvnw.cmd spring-boot:run
③ test-chat.ps1:一发请求验证服务真的活了(用 Invoke-RestMethod 绕开 curl 的引号问题):
# test-chat.ps1 —— 冒烟测试:服务能收到请求并返回模型回答
try {
$r = Invoke-RestMethod -Uri http://localhost:8080/api/v1/chat/ask `
-Method Post -ContentType "application/json" `
-Body '{"question":"只回答两个字:收到"}'
Write-Host "成功:" -ForegroundColor Green; $r
} catch {
Write-Host "失败:$_" -ForegroundColor Red
Write-Host "排查:① 应用起来了吗(8080 是否监听)② 密钥非空吗 ③ 端口是否被别的程序占用" -ForegroundColor Yellow
}
运行方式(在脚本所在目录打开 PowerShell):
powershell -ExecutionPolicy Bypass -File .\stop-app.ps1
powershell -ExecutionPolicy Bypass -File .\start-app.ps1
powershell -ExecutionPolicy Bypass -File .\test-chat.ps1
.ps1 脚本里有中文,一跑就报语法错误或中文变问号。
根因:Windows PowerShell 5.1 默认按 ANSI(GBK) 解析没有 BOM 的文件;而编辑器通常存成 UTF-8 无 BOM,于是中文注释和中文字符串被按 GBK 解码,直接破坏语法。
两种解法:① 脚本里只用 ASCII(注释和数据都写英文)——最省事,课程脚本都这么做;② 用 UTF-8 with BOM 保存(VS Code 右下角编码处选 "UTF-8 with BOM")。中文测试数据不要直接写进命令行或脚本,放进单独的 UTF-8 JSON 文件,用 -Body (Get-Content -Raw -Encoding UTF8 body.json) 读进来——这条在后续章节会被反复用到。
0.3安装 JDK 17 并验证
-
下载并安装 JDK 17(推荐 Temurin 17,免费、无登录;Oracle 版本也可以)。安装时记下安装路径,例如 Windows 的
C:\Program Files\Java\jdk-17。 -
配置环境变量
JAVA_HOME指向安装根目录,并把%JAVA_HOME%\bin(macOS/Linux 为$JAVA_HOME/bin)加入Path。IDEA 通常能自动识别,但命令行构建(第 11 章打包部署)依赖它。 -
关掉所有已打开的终端窗口,重新开一个(环境变量改完不重开终端不生效——这是新手第一大坑),执行:
java -version看到类似下面的输出即为成功:
openjdk version "17.0.x" 2025-xx-xx OpenJDK Runtime Environment Temurin-17.0.x ... OpenJDK 64-Bit Server VM Temurin-17.0.x ...
java 不是内部或外部命令。按顺序检查:① 环境变量改完后终端有没有重开?② Path 里加的是 bin 目录还是安装根目录(必须是 bin)?③ 机器上是否装了多个 JDK、Path 里被别的版本抢先?用 where java(Windows)查看实际生效的是哪一个。
0.4安装 IDEA
- 下载 IDEA Community(社区版),安装时勾选「创建桌面快捷方式」和「添加到 PATH」(可选)。
- 首次启动选择你喜欢的主题即可,不需要安装任何 AI 相关插件。本课程不使用 Lombok(所有 getter/setter 用 Java record 或手写,目的是让学生看全每一行代码),所以也不装 Lombok 插件。
- 中文界面插件可选装(Plugins → 搜索 Chinese),不影响任何功能。
0.5配置 Maven(国内镜像,必做)
Maven 默认从国外中央仓库下载依赖,国内直连速度慢甚至超时。这一步不做,第 1 章第一次构建就会卡死在下载依赖上。
-
找到 Maven 的配置文件
settings.xml:- 独立安装的 Maven:在安装目录
conf/settings.xml; - 建议复制一份到
~/.m2/settings.xml(用户级配置,升级 Maven 不丢失)。
- 独立安装的 Maven:在安装目录
-
在
<settings>标签内加入以下两段(完整文件可直接照抄):<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"> <!-- ① 本地仓库路径:依赖 jar 包的存放位置。 默认在 C 盘用户目录,建议改到 D 盘等其他数据盘 --> <localRepository>D:/maven-repo</localRepository> <mirrors> <!-- ② 阿里云公共仓库:国内下载依赖快且稳 --> <mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors> </settings> -
验证(重开终端):
mvn -v输出里确认两行:Maven 版本 ≥ 3.6.3,Java version 是 17。Maven 运行用的 JDK 和
JAVA_HOME指向的一致,如果这里显示的不是 17,回去检查JAVA_HOME。 -
IDEA 侧确认:File → Settings → Build Tools → Maven,把 Maven home path 指向你的 Maven 目录,User settings file 勾选 Override 并指向刚改的
settings.xml。
mvn 仍然解析到旧版——构建时报 xxx-plugin requires Maven version 3.6.3(插件要求新版,实际跑的却是旧版)。
排查三步:
① mvn -v 看版本是否符合预期;
② where mvn(Windows)看命令实际解析到哪个路径;
③ 解决任选其一:用项目自带 Wrapper .\mvnw.cmd(最推荐,锁定构建版本)、写完整路径调用、或从系统 PATH 删掉旧条目后新开终端。
另外注意:IDEA 的 Maven home 设置只影响 IDEA 内部构建,管不到外部终端;改完环境变量必须重开终端/重启 IDEA,运行中的进程不会刷新。
0.6申请 DeepSeek API Key
课程默认使用 DeepSeek(便宜、OpenAI 协议兼容、注册即用)。流程五步:
- 打开 deepseek.com,右上角进入「API 开放平台」,用手机号注册登录(是开放平台,不是聊天 App)。
- 左侧「充值」:先充 1~10 元即可。deepseek-chat 计价极低,做完全部课程实验花不了几块钱。
- 左侧「API keys」→「创建 API key」,名称随意(如
springai-course)。 - 创建后立刻复制保存——弹窗关掉后就再也看不到完整 key 了,只能重新创建。key 形如
sk-63956d56xxxxxxxxxxa3e4c2fc。 - 把 key 设置为环境变量(见下),不要写在任何源码文件里。
Windows(PowerShell,设置后重开终端生效):
setx DEEPSEEK_API_KEY "sk-你的完整key"
macOS / Linux(写入 ~/.zshrc 或 ~/.bashrc 后 source 生效):
export DEEPSEEK_API_KEY="sk-你的完整key"
setx 之前启动的,它以及它带起来的应用进程里根本没有这个变量——典型症状:应用启动正常、接口也能访问,一调模型就 401。
解决(二选一):
① 完全退出并重启 IDEA(最简单);
② 在 IDEA 的 Run Configuration → Environment variables 里手动添加。
验证:项目里临时打印 System.getenv("DEEPSEEK_API_KEY"),输出为空就说明进程没继承到,重开即好。
application.yml 的 ${DEEPSEEK_API_KEY:} 占位符里,绝不硬编码;
② 代码仓库上传前必须确认密钥不在任何文件里(key 一旦进 Git 历史,即使删除也还在);
③ 密钥泄露(比如不小心发到群里)立即去平台删除重建。
真实事故参考:无数公司的云账单爆炸,都是从「一个 key 提交到了 GitHub」开始的。
0.7创建课程工程(两种方式任选)
方式 A(推荐):使用课程配套工程 springai-lab
课程提供配套可运行工程 springai-lab,与教程代码逐字一致、每行都有中文注释,已随本课程包放在本教程目录的 lab/ 文件夹。使用:
- IDEA → Open → 选中
lab目录(认准有pom.xml的那层)打开; - 等待右下角 Maven 同步完成(首次会下载依赖,配好镜像约 1~3 分钟);
- 检查
src/main/resources/application.yml中密钥占位符与你的环境变量名一致。
方式 B:从零创建(体验完整流程)
- 打开 start.spring.io,按下表选择:
| 选项 | 选择 |
|---|---|
| Project | Maven |
| Language | Java |
| Spring Boot | 4.1.x(不要选 3.x,更不要选 SNAPSHOT) |
| Group / Artifact | com.example / springai-lab |
| Package name | com.example.springai |
| Packaging / Java | Jar / 17 |
| Dependencies | Spring Web + Spring AI Model Chat OpenAI(在 AI 区域) |
- Generate 下载 zip,解压后用 IDEA 打开;
- 手动在
pom.xml补上spring-ai-bom的dependencyManagement段(第 1 章会逐行讲解为什么必须有它)。
0.8全环境验收:三条命令
以下三条全部通过,环境才算就绪。课上教师逐人检查第 ①② 条的输出。
# ① JDK 版本(期望 17.x)
java -version
# ② Maven 版本 + 用的 JDK(期望 Maven ≥ 3.6.3,Java 17)
mvn -v
# ③ 环境变量里的密钥已就位(期望打印出 sk- 开头的 key,而不是空)
# PowerShell:
echo $env:DEEPSEEK_API_KEY
# macOS / Linux:
echo $DEEPSEEK_API_KEY
0.9常见问题排错表
| 现象 | 原因 | 解决 |
|---|---|---|
java -version 显示的不是 17 | 装了多个 JDK,Path 顺序问题 | 把 %JAVA_HOME%\bin 上移到 Path 最前;重开终端 |
mvn -v 报错或 Java 版本不对 | Maven 用了别的 JDK | 检查 JAVA_HOME 指向 17 的安装根目录 |
| IDEA 里依赖一直转圈/下载失败 | 没配国内镜像,或 IDEA 没用你的 settings.xml | 按 0.5 节检查 IDEA 的 Maven 配置;右下角 Maven 面板点刷新 |
Boot 4 插件要求 Maven 版本类报错 | Maven 太老(如 3.6.1) | 升级到 3.9.x |
| 程序启动报 401 | 密钥没读到(环境变量没重开终端)或 key 无效 | 0.8 节命令 ③ 确认;确认充值到账、key 复制完整 |
端口 8080 被占用(Port already in use) | 上一个应用没停,或别的程序占了 | 找到占用进程结束;或临时改 application.yml 的 server.port |
| setx 后 echo 还是空 | 终端没重开 | 关掉所有终端重开;IDEA 也要重启才能读到新环境变量(见 0.6 节「进程继承」说明) |
mvn -v 版本不对 / 构建报插件要求更高 Maven 版本 | 机器装了多个 Maven,系统 PATH 优先于用户 PATH,旧版抢先 | 见 0.5 节末尾「真实案例」:用 where mvn 定位,改用 mvnw.cmd 或删旧条目后重开终端 |
在工程上层目录执行 mvn spring-boot:run 报 No plugin found for prefix 'spring-boot' | 当前目录没有 pom.xml(工程在子目录里) | 先 cd 到 pom.xml 所在目录再执行;或用 mvn -f 子目录/pom.xml spring-boot:run |
PowerShell 里 curl 发 JSON 报 400,日志显示请求体 {question:hi} | PS 5.1 传参不转义内嵌双引号;且 curl 被解析成 Invoke-WebRequest 别名 | 换 Apifox;或用 Invoke-RestMethod;或写 curl.exe 并在 Git Bash / CMD 中执行(见 0.2 节对照表) |
PowerShell 报 -Dincludes=... 参数不识别 | PS 会把 -D 拆坏 | 改用 --define "includes=org.springframework.ai" |
自己写的 .ps1 含中文就报语法错误 | PS 5.1 按 GBK 解析 UTF-8 无 BOM 文件 | 脚本内只用 ASCII;或另存为 UTF-8 with BOM。中文数据放 JSON 文件读取 |
0.10课堂练习与课后作业
课堂练习(课上完成):
- 互相交换检查 0.8 节三条命令的输出并截图;
- 故意把
JAVA_HOME改错,观察mvn -v报什么,再改回来——知道错误长什么样,比记住正确答案更重要。
课后作业:
- 完成方式 B 的工程创建(即使你用方式 A),目的:熟悉 Initializr 每个选项的含义,写 200 字说明「Spring Web 和 Spring AI Model Chat OpenAI 两个依赖分别给项目带来了什么」;
- 阅读配套工程
lab/pom.xml的注释,圈出三处你看不懂的地方,下次课提问。
0.11本章验收标准
java -version、mvn -v、密钥 echo 三条命令输出全部符合预期(截图留存)。- lab 工程能在 IDEA 中打开,Maven 同步无红线。
- 能口头说出:本课程的版本基准是什么、为什么密钥不能写进源码。
- 完成方式 B 的工程创建并提交 200 字依赖说明。