Skip to content
第 44 / 250 章后端⏱ 10 分钟阅读

第 44 章:第一个 REST 接口

学习目标

  • Spring InitializrIntelliJ IDEA 两种方式创建 SpringBoot 项目
  • 理解 pom.xml 的结构与依赖管理
  • 掌握 @SpringBootApplication@RestController@GetMapping 三个核心注解
  • 实现并测试第一个 REST 接口

一、从需求出发:我们要做什么?

我们要写一个最简单的 Web 接口:

bash
# 浏览器访问
$ curl http://localhost:8080/api/hello
# 返回:Hello, SpringBoot!

看似简单,背后藏着 SpringBoot 的核心机制:

接下来一步步实现

二、方式 A:用 Spring Initializr 创建项目(推荐新手)

Spring Initializr 是官方提供的项目脚手架,能帮你生成标准化的项目结构。

步骤 1:访问 Spring Initializr

打开 https://start.spring.io/,按下面配置:

详细配置

配置项说明
ProjectMaven构建工具(Maven vs Gradle)
LanguageJava编程语言
Spring Boot3.2.x当前稳定版
Project MetadataGroup: com.taskflow
Artifact: hello-rest
Name: hello-rest
Package Name: com.taskflow.hellorest
Packaging: Jar
Java: 17
项目坐标
DependenciesSpring 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 打开

  1. IDEA → Open → 选择 hello-rest 目录
  2. IDEA 自动识别为 Maven 项目,开始下载依赖(首次需要几分钟)

三、方式 B:用 IntelliJ IDEA 直接创建(更方便)

  1. IDEA → New Project
  2. 选择 Spring Initializr(左侧面板)
  3. Server URL: https://start.spring.io(默认)
  4. 点击 Next
  5. 填写 Project Metadata(与方式 A 相同)
  6. 选择依赖:勾选 Spring Web
  7. Finish

IDEA 会自动生成项目并打开。

四、pom.xml 详解

📦 完整示例位于仓库:examples/springboot-hello-rest/pom.xml

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-webWeb 应用(Tomcat + Spring MVC + Jackson)
spring-boot-starter-data-jpaJPA + Hibernate + 数据源
spring-boot-starter-securitySpring Security
spring-boot-starter-testJUnit + Mockito + AssertJ + Spring Test
spring-boot-starter-actuator生产监控端点
spring-boot-starter-validationJSR-303 参数校验
spring-boot-starter-cacheSpring Cache 抽象
spring-boot-starter-data-redisRedis 集成

五、主类详解

java
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() 背后做了大量工作:

  1. 创建 Spring IoC 容器(ApplicationContext)
  2. 执行自动配置(@EnableAutoConfiguration)
  3. 扫描组件(@ComponentScan)
  4. 启动内嵌 Tomcat(默认端口 8080)
  5. 注册各种 Bean
  6. 加载配置文件(application.yml)
  7. 初始化日志系统

这一行代码相当于把整个 Web 服务器拉起来。

六、第一个 Controller

创建 Controller 类

com.taskflow.hellorest 包下创建 HelloController.java

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));
    }
}

逐行注释

行号代码含义
@RestControllerREST 控制器,返回值直接写入 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(更易读):

yaml
# ============================================================
# 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 对比

yaml
# application.yml(推荐:层级清晰)
server:
  port: 8080
  servlet:
    context-path: /
properties
# application.properties(更老,配置项多时容易乱)
server.port=8080
server.servlet.context-path=/

yml 优势:层级结构、可读性强、支持列表/Map 语法。

八、启动应用

方式 A:IDEA 直接启动

  1. 找到 HelloRestApplication.java
  2. 点击 main 方法左边的绿色三角箭头 ▶
  3. 选择 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 命令启动

bash
# 项目根目录执行
mvn spring-boot:run

方式 C:打包后启动

bash
# 打包成 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+)

bash
# 测试 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 文件:

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监听端口、接收请求、返回响应
DispatcherServletSpring MVC 的前端控制器,负责请求分发
HandlerMapping根据 URL 找到对应的 Controller 方法
HttpMessageConverter把返回值(如 String)转换为 HTTP 响应(如 text/plain)

十一、常见问题排查

Q1: 启动报错 "Web server failed to start. Port 8080 was already in use."

原因:8080 端口被占用。

解决

bash
# 方案 A:杀掉占用 8080 端口的进程
# Linux/macOS
$ lsof -i :8080
$ kill -9 <PID>

# Windows
> netstat -ano | findstr :8080
> taskkill /PID <PID> /F

# 方案 B:换端口
# application.yml
server:
  port: 9090

Q2: 浏览器访问 404

排查步骤

  1. 确认应用已启动:控制台看到 Started HelloRestApplication
  2. 确认端口正确:默认 8080,看配置有没有改
  3. 确认 URL 正确/api/hello 不是 /api/Hello
  4. 确认类被扫描:Controller 必须在 com.taskflow.hellorest 包下或其子包下(因为主类在 com.taskflow.hellorest

Q3: 修改 Controller 代码不生效

原因:没有开启自动重启。

解决:在 pom.xml 添加 spring-boot-devtools

xml
<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,如果还乱码:

yaml
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 里添加一个新接口:

java
/**
 * 计算两个数的和
 *
 * 完整 URL:http://localhost:8080/api/add?a=3&b=5
 * 期望返回:8
 */

提示:用 @RequestParam 接收两个整数参数。

练习 2:返回 JSON(必做)

写一个接口 /api/user,返回一个用户信息:

java
@GetMapping("/user")
public User getUser() {
    return new User(1L, "张三", "zhangsan@example.com");
}

// User 类
public record User(Long id, String name, String email) {}

期望响应(JSON):

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:环境切换(挑战)

  1. 创建 application-dev.ymlapplication-prod.yml
  2. application.yml 里指定默认激活 dev:
yaml
spring:
  profiles:
    active: dev
  1. 在 dev 配置里端口设为 8080,prod 配置里端口设为 80
  2. 启动时切换:
bash
# 默认 dev
mvn spring-boot:run

# 切换 prod
mvn spring-boot:run -Dspring-boot.run.profiles=prod

推荐阅读


下一章第 45 章:Maven 实战

你已经完成了

  • ✅ 用 Spring Initializr 创建 SpringBoot 项目
  • ✅ 理解 pom.xml 的结构和继承关系
  • ✅ 掌握 @SpringBootApplication 三大注解
  • ✅ 实现 3 个 REST 接口(hello / greeting / time)
  • ✅ 用 curl 和浏览器测试接口
  • ✅ 学会切换 dev / prod 环境

本站基于 VitePress 构建 · 由 Codebook 团队维护