第 44 章:第一个 REST 接口
学习目标
- 用 Spring Initializr 和 IntelliJ IDEA 两种方式创建 SpringBoot 项目
- 理解
pom.xml的结构与依赖管理 - 掌握
@SpringBootApplication、@RestController、@GetMapping三个核心注解 - 实现并测试第一个 REST 接口
一、从需求出发:我们要做什么?
我们要写一个最简单的 Web 接口:
# 浏览器访问
$ curl http://localhost:8080/api/hello
# 返回:Hello, SpringBoot!看似简单,背后藏着 SpringBoot 的核心机制:
接下来一步步实现。
二、方式 A:用 Spring Initializr 创建项目(推荐新手)
Spring Initializr 是官方提供的项目脚手架,能帮你生成标准化的项目结构。
步骤 1:访问 Spring Initializr
打开 https://start.spring.io/,按下面配置:
详细配置:
| 配置项 | 值 | 说明 |
|---|---|---|
| Project | Maven | 构建工具(Maven vs Gradle) |
| Language | Java | 编程语言 |
| Spring Boot | 3.2.x | 当前稳定版 |
| Project Metadata | Group: com.taskflowArtifact: hello-restName: hello-restPackage Name: com.taskflow.hellorestPackaging: Jar Java: 17 | 项目坐标 |
| Dependencies | Spring Web | 我们要写 Web 接口 |
步骤 2:点击 Generate 下载
得到 hello-rest.zip,解压后得到如下结构:
hello-rest/
├── .gitignore ← Git 忽略文件
├── .mvn/ ← Maven Wrapper(可选)
├── mvnw ← Linux/Mac 的 Maven Wrapper
├── mvnw.cmd ← Windows 的 Maven Wrapper
├── pom.xml ← 项目对象模型
└── src/
├── main/
│ ├── java/
│ │ └── com/taskflow/hellorest/
│ │ └── HelloRestApplication.java ← 主类
│ └── resources/
│ ├── application.properties ← 配置文件(也可能是 .yml)
│ ├── static/ ← 静态资源
│ └── templates/ ← 模板文件
└── test/
└── java/
└── com/taskflow/hellorest/
└── HelloRestApplicationTests.java ← 测试类步骤 3:用 IDEA 打开
- IDEA → Open → 选择
hello-rest目录 - IDEA 自动识别为 Maven 项目,开始下载依赖(首次需要几分钟)
三、方式 B:用 IntelliJ IDEA 直接创建(更方便)
- IDEA → New Project
- 选择 Spring Initializr(左侧面板)
- Server URL:
https://start.spring.io(默认) - 点击 Next
- 填写 Project Metadata(与方式 A 相同)
- 选择依赖:勾选 Spring Web
- Finish
IDEA 会自动生成项目并打开。
四、pom.xml 详解
📦 完整示例位于仓库:
examples/springboot-hello-rest/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- ============================================================
项目模型版本号(Maven 4 默认 4.0.0)
============================================================ -->
<modelVersion>4.0.0</modelVersion>
<!-- ============================================================
父 POM:spring-boot-starter-parent
============================================================
作用:继承 SpringBoot 的默认配置(依赖版本、插件配置等)
这样我们引入 spring-boot-starter-* 时不需要写版本号
============================================================ -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version> <!-- ① SpringBoot 版本 -->
<relativePath/> <!-- ② 从仓库查找,不本地 -->
</parent>
<!-- ============================================================
项目坐标(GAV)
============================================================ -->
<groupId>com.taskflow</groupId> <!-- ③ 组织域名 -->
<artifactId>hello-rest</artifactId> <!-- ④ 项目名 -->
<version>0.0.1-SNAPSHOT</version> <!-- ⑤ 版本(SNAPSHOT = 开发版) -->
<name>hello-rest</name> <!-- ⑥ 显示名 -->
<description>我的第一个 SpringBoot REST 接口</description> <!-- ⑦ 描述 -->
<!-- ============================================================
属性:定义全局变量
============================================================ -->
<properties>
<java.version>17</java.version> <!-- ⑧ Java 版本 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<!-- ============================================================
依赖列表
============================================================ -->
<dependencies>
<!-- ⑨ Web 启动器:一个依赖包含 Tomcat + Spring MVC + Jackson + 验证器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 没有 version:因为父 POM 已经管理了版本 -->
</dependency>
<!-- ⑩ 测试启动器(开发期用) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <!-- ⑪ 只在 test 阶段生效 -->
</dependency>
</dependencies>
<!-- ============================================================
构建配置
============================================================ -->
<build>
<plugins>
<!-- ⑫ SpringBoot Maven 插件:打包成可执行 jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>关键概念
| 编号 | 元素 | 含义 |
|---|---|---|
| ① | <version>3.2.0</version> | SpringBoot 版本,父 POM 自动管理所有 starter 版本 |
| ③ | <groupId> | 组织域名(反写),如 com.taskflow |
| ④ | <artifactId> | 项目名 |
| ⑤ | <version>0.0.1-SNAPSHOT</version> | 版本,SNAPSHOT 表示开发中 |
| ⑧ | <java.version>17</java.version> | 告诉 Maven 用 Java 17 编译 |
| ⑨ | starter-web | 一个依赖 = Web 完整生态(Tomcat + MVC + JSON) |
| ⑪ | <scope>test</scope> | 范围:test 表示只在测试阶段加入 classpath |
| ⑫ | spring-boot-maven-plugin | 打包成可执行 jar,包含 main 方法和所有依赖 |
SpringBoot Starter 是什么?
Starter = 预打包的依赖集合。引入一个 starter,自动获得一整套相关依赖。常见的 starter:
| Starter | 包含内容 |
|---|---|
spring-boot-starter-web | Web 应用(Tomcat + Spring MVC + Jackson) |
spring-boot-starter-data-jpa | JPA + Hibernate + 数据源 |
spring-boot-starter-security | Spring Security |
spring-boot-starter-test | JUnit + Mockito + AssertJ + Spring Test |
spring-boot-starter-actuator | 生产监控端点 |
spring-boot-starter-validation | JSR-303 参数校验 |
spring-boot-starter-cache | Spring Cache 抽象 |
spring-boot-starter-data-redis | Redis 集成 |
五、主类详解
package com.taskflow.hellorest; // ① 包路径
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* SpringBoot 应用主类
*
* @SpringBootApplication 是一个组合注解,包含:
* - @SpringBootConfiguration:标记这是一个 SpringBoot 配置类
* - @EnableAutoConfiguration:开启自动配置(SpringBoot 的核心特性)
* - @ComponentScan:自动扫描当前包及其子包下的所有 Spring 组件
*/
@SpringBootApplication // ② 主类注解(核心)
public class HelloRestApplication { // ③ 类名(随意,但建议含 Application 后缀)
public static void main(String[] args) { // ④ 标准 main 方法
SpringApplication.run( // ⑤ 启动 SpringBoot 应用
HelloRestApplication.class, // ⑥ 传入主类
args // ⑦ 命令行参数
);
}
}关键设计决策
为什么 main 方法里只调用一行就能启动整个应用?
SpringApplication.run()背后做了大量工作:
- 创建 Spring IoC 容器(ApplicationContext)
- 执行自动配置(@EnableAutoConfiguration)
- 扫描组件(@ComponentScan)
- 启动内嵌 Tomcat(默认端口 8080)
- 注册各种 Bean
- 加载配置文件(application.yml)
- 初始化日志系统
这一行代码相当于把整个 Web 服务器拉起来。
六、第一个 Controller
创建 Controller 类
在 com.taskflow.hellorest 包下创建 HelloController.java:
package com.taskflow.hellorest.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 第一个 REST 控制器
*
* 什么是 REST?
* - REST 是一种 Web API 设计风格(不是框架也不是标准)
* - 用 HTTP 方法(GET/POST/PUT/DELETE)对应 CRUD 操作
* - GET /users = 查询
* - POST /users = 创建
* - PUT /users/{id} = 更新
* - DELETE /users/{id}= 删除
*
* @RestController = @Controller + @ResponseBody
* - @Controller:标记这是 Spring MVC 控制器
* - @ResponseBody:方法返回值直接写入 HTTP 响应体(不经过视图解析器)
*/
@RestController // ① 标记为 REST 控制器
@RequestMapping("/api") // ② 类级别 URL 前缀
public class HelloController {
/**
* 简单的 hello 接口
*
* 完整 URL:http://localhost:8080/api/hello
* HTTP 方法:GET
*/
@GetMapping("/hello") // ③ 映射 GET /api/hello
public String hello() {
return "Hello, SpringBoot!"; // ④ 返回的字符串直接作为响应体
}
/**
* 带路径变量的接口
*
* 完整 URL:http://localhost:8080/api/greeting/张三
* HTTP 方法:GET
*/
@GetMapping("/greeting/{name}") // ⑤ {name} 是路径变量
public String greeting(@PathVariable String name) { // ⑥ 接收路径变量
return "Hello, " + name + "!"; // ⑦ 拼接返回值
}
/**
* 带查询参数的接口
*
* 完整 URL:http://localhost:8080/api/time?format=yyyy-MM-dd
* HTTP 方法:GET
*/
@GetMapping("/time") // ⑧ 映射 GET /api/time
public String time(@RequestParam(defaultValue = "yyyy-MM-dd HH:mm:ss") String format) {
// ⑨ @RequestParam 接收查询参数,defaultValue 是默认值
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));
}
}逐行注释
| 行号 | 代码 | 含义 |
|---|---|---|
| ① | @RestController | REST 控制器,返回值直接写入 HTTP 响应体 |
| ② | @RequestMapping("/api") | 类级 URL 前缀,所有方法都加 /api |
| ③ | @GetMapping("/hello") | 映射 GET 请求,完整路径 /api/hello |
| ⑤ | @GetMapping("/greeting/{name}") | {name} 是占位符,会被实际值替换 |
| ⑥ | @PathVariable | 把 URL 里的占位符绑定到方法参数 |
| ⑧ | @GetMapping("/time") | 映射 GET 请求,完整路径 /api/time |
| ⑨ | @RequestParam | 把 URL 查询参数绑定到方法参数 |
关键注解拆解
为什么用 @RestController 而不是 @Controller?
@Controller:返回值会被解析为视图名(如 Thymeleaf 模板),适合服务端渲染@RestController:返回值直接写入 HTTP 响应体(JSON / 字符串),适合前后端分离现代企业项目都是前后端分离,所以默认用
@RestController。
七、application.yml 配置
把默认的 application.properties 改名为 application.yml(更易读):
# ============================================================
# SpringBoot 应用配置
# ============================================================
# 配置文件优先级(从高到低):
# 1. 命令行参数:--server.port=9090
# 2. 环境变量:SERVER_PORT=9090
# 3. application-{profile}.yml(按激活的环境)
# 4. application.yml(默认)
# ============================================================
# 服务器配置
server:
port: 8080 # ① HTTP 端口
servlet:
context-path: / # ② URL 前缀(这里设为根)
# 应用信息
spring:
application:
name: hello-rest # ③ 应用名(日志、监控里会用到)
# 日志配置
logging:
level:
com.taskflow: DEBUG # ④ 我们包的日志级别设为 DEBUG
org.springframework: INFO # ⑤ Spring 框架日志设为 INFO(少一点噪音)yml vs properties 对比
# application.yml(推荐:层级清晰)
server:
port: 8080
servlet:
context-path: /# application.properties(更老,配置项多时容易乱)
server.port=8080
server.servlet.context-path=/yml 优势:层级结构、可读性强、支持列表/Map 语法。
八、启动应用
方式 A:IDEA 直接启动
- 找到
HelloRestApplication.java - 点击
main方法左边的绿色三角箭头 ▶ - 选择 Run 'HelloRestApplication'
控制台输出:
实际日志:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
2026-08-12 10:30:42.123 INFO 12345 --- [ main] c.t.hellorest.HelloRestApplication : Starting Java version 17
2026-08-12 10:30:42.456 INFO 12345 --- [ main] c.t.hellorest.HelloRestApplication : Starting HelloRestApplication using Java 17.0.13
2026-08-12 10:30:43.789 INFO 12345 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2026-08-12 10:30:44.012 INFO 12345 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-08-12 10:30:44.234 INFO 12345 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.16]
2026-08-12 10:30:44.456 INFO 12345 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2026-08-12 10:30:45.123 INFO 12345 --- [ main] c.t.hellorest.HelloRestApplication : Started HelloRestApplication in 2.345 seconds (process running for 3.456)关键信息:
Started HelloRestApplication in 2.345 seconds:启动耗时Tomcat initialized with port 8080:内嵌 Tomcat 启动在 8080 端口
方式 B:Maven 命令启动
# 项目根目录执行
mvn spring-boot:run方式 C:打包后启动
# 打包成 jar
mvn clean package
# 生成 target/hello-rest-0.0.1-SNAPSHOT.jar
# 运行
java -jar target/hello-rest-0.0.1-SNAPSHOT.jar九、测试接口
方式 A:curl(Linux/macOS / Windows 10+)
# 测试 hello 接口
$ curl http://localhost:8080/api/hello
Hello, SpringBoot!
# 测试 greeting 接口(带路径变量)
$ curl http://localhost:8080/api/greeting/张三
Hello, 张三!
# 测试 time 接口(带查询参数)
$ curl "http://localhost:8080/api/time?format=yyyy年MM月dd日"
2026年08月12日
# 查看 HTTP 响应头(verbose 模式)
$ curl -v http://localhost:8080/api/hello
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080
> GET /api/hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200
< Content-Type: text/plain;charset=UTF-8 ← Spring 默认的响应类型
< Content-Length: 19
<
Hello, SpringBoot! ← 响应体方式 B:浏览器
直接在浏览器输入:
http://localhost:8080/api/hello
http://localhost:8080/api/greeting/张三
http://localhost:8080/api/time浏览器会自动 URL 编码中文。如果中文显示乱码,是浏览器编码问题,不是 SpringBoot 的问题。
方式 C:IDEA HTTP Client
IDEA 内置 HTTP Client,创建 test.http 文件:
### 测试 hello 接口
GET http://localhost:8080/api/hello
### 测试 greeting 接口
GET http://localhost:8080/api/greeting/张三
### 测试 time 接口
GET http://localhost:8080/api/time?format=yyyy-MM-dd
### 测试 time 接口(自定义格式)
GET http://localhost:8080/api/time?format=yyyy年MM月dd日 HH时mm分点击绿色箭头 ▶ 即可发送请求,IDEA 下方会显示响应。
十、深入理解:请求是怎么被处理的?
关键组件:
| 组件 | 职责 |
|---|---|
| Tomcat | 监听端口、接收请求、返回响应 |
| DispatcherServlet | Spring MVC 的前端控制器,负责请求分发 |
| HandlerMapping | 根据 URL 找到对应的 Controller 方法 |
| HttpMessageConverter | 把返回值(如 String)转换为 HTTP 响应(如 text/plain) |
十一、常见问题排查
Q1: 启动报错 "Web server failed to start. Port 8080 was already in use."
原因:8080 端口被占用。
解决:
# 方案 A:杀掉占用 8080 端口的进程
# Linux/macOS
$ lsof -i :8080
$ kill -9 <PID>
# Windows
> netstat -ano | findstr :8080
> taskkill /PID <PID> /F
# 方案 B:换端口
# application.yml
server:
port: 9090Q2: 浏览器访问 404
排查步骤:
- 确认应用已启动:控制台看到
Started HelloRestApplication - 确认端口正确:默认 8080,看配置有没有改
- 确认 URL 正确:
/api/hello不是/api/Hello - 确认类被扫描:Controller 必须在
com.taskflow.hellorest包下或其子包下(因为主类在com.taskflow.hellorest)
Q3: 修改 Controller 代码不生效
原因:没有开启自动重启。
解决:在 pom.xml 添加 spring-boot-devtools:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>并在 IDEA 里开启自动编译: Settings → Build, Execution, Deployment → Compiler → Build project automatically
Q4: 中文乱码
解决:SpringBoot 3.x 默认就是 UTF-8,如果还乱码:
spring:
http:
encoding:
charset: UTF-8
enabled: true
force: true十二、本章小结
| 要点 | 关键 |
|---|---|
| 项目创建 | 用 Spring Initializr(网页)或 IDEA New Project |
| pom.xml | 父 POM 继承 starter-parent,引入 starter-web |
| 主类 | @SpringBootApplication + SpringApplication.run() |
| Controller | @RestController + @GetMapping + @PathVariable / @RequestParam |
| 配置 | application.yml 替代 properties,更易读 |
| 启动 | IDEA ▶ 按钮或 mvn spring-boot:run |
| 测试 | curl / 浏览器 / IDEA HTTP Client |
| 调试 | 默认端口 8080,关注控制台启动日志 |
动手练习
练习 1:扩展接口(必做)
在 HelloController 里添加一个新接口:
/**
* 计算两个数的和
*
* 完整 URL:http://localhost:8080/api/add?a=3&b=5
* 期望返回:8
*/提示:用 @RequestParam 接收两个整数参数。
练习 2:返回 JSON(必做)
写一个接口 /api/user,返回一个用户信息:
@GetMapping("/user")
public User getUser() {
return new User(1L, "张三", "zhangsan@example.com");
}
// User 类
public record User(Long id, String name, String email) {}期望响应(JSON):
{"id":1,"name":"张三","email":"zhangsan@example.com"}为什么用 record 而不是 class?
Java 14 引入的 record 是不可变数据类的语法糖。record User(Long id, String name, String email) 自动生成:
- 构造器
- Getter 方法(
user.id()、user.name()、user.email()) equals()、hashCode()、toString()
适合 DTO / VO / 实体类。后续章节会大量使用。
练习 3:环境切换(挑战)
- 创建
application-dev.yml和application-prod.yml - 在
application.yml里指定默认激活 dev:
spring:
profiles:
active: dev- 在 dev 配置里端口设为 8080,prod 配置里端口设为 80
- 启动时切换:
# 默认 dev
mvn spring-boot:run
# 切换 prod
mvn spring-boot:run -Dspring-boot.run.profiles=prod推荐阅读
- 🌐 Spring Boot Reference Documentation — 官方权威文档
- 🌐 Spring Initializr — 在线项目生成器
- 📖 《SpringBoot 实战》第 4 版 — 第 1-3 章讲入门
下一章:第 45 章:Maven 实战 →
你已经完成了:
- ✅ 用 Spring Initializr 创建 SpringBoot 项目
- ✅ 理解 pom.xml 的结构和继承关系
- ✅ 掌握
@SpringBootApplication三大注解 - ✅ 实现 3 个 REST 接口(hello / greeting / time)
- ✅ 用 curl 和浏览器测试接口
- ✅ 学会切换 dev / prod 环境