1. 背景介绍


RESTful API 是现代软件开发中的一种常见技术,它基于 REST 架构,提供了一种轻量级、灵活的方式来构建和访问网络资源。Spring Boot 是一个用于构建 Spring 应用程序的框架,它简化了开发过程,使得开发者可以更快地构建高质量的应用程序。

在本文中,我们将讨论如何使用 Spring Boot 开发 RESTful API,包括其核心概念、算法原理、最佳实践、应用场景、工具和资源推荐以及未来发展趋势。

2. 核心概念与联系

2.1 RESTful API


RESTful API 是一种基于 HTTP 协议的网络架构风格,它提供了一种简单、灵活、可扩展的方式来构建和访问网络资源。RESTful API 的核心概念包括:

资源(Resource):网络资源是 RESTful API 的基本组成部分,它们可以是文件、数据库记录、服务等。
URI(Uniform Resource Identifier):用于唯一标识资源的字符串。
HTTP 方法:用于操作资源的方法,例如 GET、POST、PUT、DELETE 等。
状态码:用于表示请求的处理结果的三位数字代码,例如 200(OK)、404(Not Found)等。


2.2 Spring Boot


Spring Boot 是一个用于构建 Spring 应用程序的框架,它提供了一系列的工具和库,使得开发者可以更快地构建高质量的应用程序。Spring Boot 的核心概念包括:

自动配置:Spring Boot 提供了一系列的自动配置,使得开发者无需手动配置应用程序的各种依赖关系和配置参数。
应用程序启动器:Spring Boot 提供了一系列的应用程序启动器,使得开发者可以快速搭建 Spring 应用程序。
依赖管理:Spring Boot 提供了一系列的依赖管理工具,使得开发者可以轻松管理应用程序的依赖关系。


3. 核心算法原理和具体操作步骤以及数学模型公式详细讲解


3.1 RESTful API 的核心算法原理


RESTful API 的核心算法原理是基于 HTTP 协议的 CRUD 操作。CRUD 操作包括:

创建(Create):使用 POST 方法创建新的资源。
读取(Read):使用 GET 方法读取资源。
更新(Update):使用 PUT 或 PATCH 方法更新资源。
删除(Delete):使用 DELETE 方法删除资源。


3.2 Spring Boot 的核心算法原理


Spring Boot 的核心算法原理是基于 Spring 框架的组件和依赖注入。Spring Boot 提供了一系列的组件,例如:

应用程序上下文:用于管理应用程序的配置和资源。
Bean 工厂:用于管理应用程序的依赖关系和组件。
自动配置:用于自动配置应用程序的各种依赖关系和配置参数。
 

4. 数学模型公式详细讲解

4.1 URI


URI(Uniform Resource Identifier)是一个用于标识资源的字符串。URI的基本格式如下:

scheme:[//[userinfo@]host[:port]][/]path[?query][#fragment]

其中,scheme表示URI的协议(如http或https),host表示资源所在的服务器,port表示服务器的端口号,path表示资源的路径,query表示请求参数,fragment表示资源内的锚点。

4.2 HTTP方法


HTTP方法是用于操作资源的请求方式,常见的HTTP方法有:

GET:用于请求资源
POST:用于创建新资源
PUT:用于更新资源
DELETE:用于删除资源


4.3 状态码


HTTP状态码是用于描述请求的处理结果的三位数字代码。常见的状态码有:

200(OK):请求成功
201(Created):请求成功并创建了新资源
400(Bad Request):请求有误
404(Not Found):请求的资源不存在
500(Internal Server Error):服务器内部错误
 

5.创建接口实践

5.1全流程

整体逻辑串起来(以"新增用户"为例)

  1. 客户端发送 POST /api/users,body 是 JSON
  2. Spring MVC 把 JSON 反序列化为 User 对象,@Valid 触发校验
  3. 校验通过 → UserController.add() 调用 userService.saveUser(user)
  4. UserServiceImpl.saveUser() 调用 userMapper.insert(user)
  5. MyBatis 执行 UserMapper.xml 中的 INSERT 语句,把 #{username} 等替换为实际值
  6. 数据库返回受影响行数 = 1,user.id 被回填
  7. Controller 返回 201 Created + 新用户 JSON
  8. 如果途中任何一步抛异常 → GlobalExceptionHandler 拦截 → 返回 400 + {"error": "..."}

D:\新建文件夹\demo4\          ← 项目根目录
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── demo4/                ← ★ 包名已改为 demo4
│   │   │               ├── Demo4Application.java   ← ★ 启动类改名
│   │   │               ├── controller/
│   │   │               │   └── UserController.java
│   │   │               ├── service/
│   │   │               │   ├── UserService.java
│   │   │               │   └── impl/
│   │   │               │       └── UserServiceImpl.java
│   │   │               ├── mapper/
│   │   │               │   └── UserMapper.java
│   │   │               ├── entity/
│   │   │               │   └── User.java
│   │   │               └── exception/
│   │   │                   └── GlobalExceptionHandler.java
│   │   └── resources/
│   │       ├── application.properties
│   │       └── mapper/                       ← ★ XML 映射文件放在这里
│   │           └── UserMapper.xml
│   └── test/                                 (测试目录可忽略)
└── ...

5.2entity/User.java(实体类 / 数据模型)

package com.example.demo4.entity;
import jakarta.validation.constraints.Max;   // 最大值校验
import jakarta.validation.constraints.Min;   // 最小值校验
import jakarta.validation.constraints.NotBlank;  // 非空非空白校验
import jakarta.validation.constraints.Size;      // 长度校验
import lombok.Data;                              // Lombok 注解,自动生成 getter/setter/toString/equals

@Data   // Lombok 神器:一个注解顶一堆模板代码(getter、setter、toString、equals、hashCode)
public class User {
    private Long id;                                         // 主键 ID,Long 长整型

    @NotBlank(message = "用户名不能为空")          // username 不能是 null、空串或纯空格
    @Size(max = 50, message = "用户名长度不能超过50")  // username 最大长度 50
    private String username;

    @Size(max = 10, message = "姓名长度不能超过10")  // name 最大长度 10(可为空)
    private String name;

    @Min(value = 0, message = "年龄不能小于0")    // age 最小值 0
    @Max(value = 150, message = "年龄不能大于150")  // age 最大值 150
    private Integer age;

    @Size(max = 1, message = "性别只能为单个字符")  // gender 最大长度 1(如 "男"/"女")
    private String gender;
}

作用:对应数据库 user 表的 Java 实体。校验注解会在 Controller 层 @Valid 时自动触发。

5.3mapper/UserMapper.java(数据访问接口)

package com.example.demo4.mapper;
import com.example.demo4.entity.User;
import org.apache.ibatis.annotations.Mapper;  // MyBatis 的 @Mapper 注解
import org.apache.ibatis.annotations.Param;    // @Param 给参数起名字,SQL 里用 #{名字} 引用
import java.util.List;

@Mapper   // 告诉 MyBatis:给这个接口生成一个代理实现类,注册为 Spring Bean
public interface UserMapper {
    int insert(User user);                         // 新增,返回受影响行数(>0 表示成功)
    int updateById(User user);                      // 根据 id 更新
    int deleteById(@Param("id") Long id);           // 根据 id 删除,@Param("id") 让 SQL 里能用 #{id}
    User selectById(@Param("id") Long id);          // 根据 id 查询单个
    List<User> selectAll();                         // 查询全部
}

作用:接口本身只有方法签名,真正的 SQL 写在 UserMapper.xml 中,MyBatis 通过方法名找到对应的 SQL 执行。

5.4service/UserService.java(业务接口)

package com.example.demo4.service;
import com.example.demo4.entity.User;
import java.util.List;

public interface UserService {
    User saveUser(User user);                // 新增用户,返回含主键的对象
    User updateUser(Long id, User user);     // 更新指定 id 的用户
    void deleteUser(Long id);                // 删除用户(无返回值)
    User getUserById(Long id);              // 查询单个用户
    List<User> getAllUsers();                // 查询所有用户
}

作用:定义业务层契约。Controller 只依赖这个接口,不关心具体实现 —— 这就是"面向接口编程",方便解耦和替换实现。

5.5service/impl/UserServiceImpl.java(业务实现)

package com.example.demo4.service.impl;
import com.example.demo4.entity.User;
import com.example.demo4.mapper.UserMapper;
import com.example.demo4.service.UserService;
import lombok.RequiredArgsConstructor;    // Lombok:为 final 字段生成构造方法
import org.springframework.stereotype.Service;  // 声明为 Service 层 Bean
import java.util.List;

@Service                  // 告诉 Spring:这是一个 Service Bean,交给容器管理
@RequiredArgsConstructor  // Lombok:给下面的 final 字段 userMapper 生成构造方法
                          // → Spring 通过构造方法自动注入 UserMapper(构造器注入,推荐方式)
public class UserServiceImpl implements UserService {
    private final UserMapper userMapper;   // final 必须在构造时赋值,由 Lombok 生成的构造方法注入

    @Override
    public User saveUser(User user) {
        int rows = userMapper.insert(user);              // 调用 Mapper 执行 INSERT
        if (rows <= 0) throw new RuntimeException("新增用户失败");  // 受影响行数<=0 说明失败
        return user;                                      // 返回 user(此时 id 已被回填)
    }

    @Override
    public User updateUser(Long id, User user) {
        User existing = userMapper.selectById(id);        // 先查一下这个 id 的用户存不存在
        if (existing == null) throw new RuntimeException("用户不存在,ID: " + id);
        user.setId(id);                                   // 把路径里的 id 设进 user 对象
        int rows = userMapper.updateById(user);           // 执行 UPDATE
        if (rows <= 0) throw new RuntimeException("更新用户失败");
        return userMapper.selectById(id);                 // 返回更新后的最新数据
    }

    @Override
    public void deleteUser(Long id) {
        if (userMapper.selectById(id) == null) throw new RuntimeException("用户不存在,ID: " + id);
        int rows = userMapper.deleteById(id);            // 执行 DELETE
        if (rows <= 0) throw new RuntimeException("删除用户失败");
    }

    @Override
    public User getUserById(Long id) {
        User user = userMapper.selectById(id);
        if (user == null) throw new RuntimeException("用户不存在,ID: " + id);
        return user;
    }

    @Override
    public List<User> getAllUsers() {
        return userMapper.selectAll();   // 直接返回全部,空表就返回空 List
    }
}

作用:业务逻辑都在这里。每个写操作(增/改/删)都做了"先校验存在 + 检查受影响行数"的双重保险。

5.6controller/UserController.java(控制器 / 接口入口)

package com.example.demo4.controller;
import com.example.demo4.entity.User;
import com.example.demo4.service.UserService;
import jakarta.validation.Valid;            // 触发实体上校验注解的开关
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;  // HTTP 状态码枚举
import org.springframework.http.ResponseEntity;  // 响应实体(包含状态码 + body)
import org.springframework.web.bind.annotation.*;  // 一次性导入所有 web 注解
import java.util.List;

@RestController              // = @Controller + @ResponseBody,返回值自动转 JSON
@RequestMapping("/api/users") // 这个类下所有接口的公共前缀都是 /api/users
@RequiredArgsConstructor     // Lombok 给 final 字段生成构造方法 → Spring 注入 userService
public class UserController {
    private final UserService userService;   // 依赖业务接口(不是实现类,面向接口编程)

    @GetMapping    // 处理 GET /api/users
    public ResponseEntity<List<User>> list() {
        return ResponseEntity.ok(userService.getAllUsers());
        // ResponseEntity.ok() → 状态码 200,body 是用户列表
    }

    @GetMapping("/{id}")   // 处理 GET /api/users/5(5 是路径变量)
    public ResponseEntity<User> getById(@PathVariable Long id) {
        // @PathVariable 把 URL 里的 {id} 提取为 Long id 参数
        return ResponseEntity.ok(userService.getUserById(id));
    }

    @PostMapping    // 处理 POST /api/users
    public ResponseEntity<User> add(@Valid @RequestBody User user) {
        // @RequestBody:把请求体 JSON 自动反序列化为 User 对象
        // @Valid:触发 User 实体上的校验注解(@NotBlank、@Size 等)
        return ResponseEntity.status(HttpStatus.CREATED).body(userService.saveUser(user));
        // 状态码 201(CREATED)表示资源创建成功,body 是新用户(含生成的 id)
    }

    @PutMapping("/{id}")   // 处理 PUT /api/users/5
    public ResponseEntity<User> update(@PathVariable Long id, @Valid @RequestBody User user) {
        return ResponseEntity.ok(userService.updateUser(id, user));  // 200 + 更新后的数据
    }

    @DeleteMapping("/{id}")  // 处理 DELETE /api/users/5
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
        // 204 No Content,body 为空,表示删除成功
    }
}

作用:对外暴露 5 个 RESTful 接口,是 HTTP 请求进入应用的入口。

5.7exception/GlobalExceptionHandler.java(全局异常处理)

package com.example.demo4.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;  // @Valid 校验失败时抛出
import org.springframework.web.bind.annotation.ExceptionHandler;     // 异常处理器注解
import org.springframework.web.bind.annotation.RestControllerAdvice; // 全局异常通知
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice   // 全局拦截所有 @RestController 抛出的异常,返回值自动转 JSON
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)   // 拦截所有 RuntimeException 及其子类
    public ResponseEntity<Map<String, String>> handleRuntime(RuntimeException ex) {
        Map<String, String> error = new HashMap<>();
        error.put("error", ex.getMessage());    // 把异常消息放进 JSON
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
        // 返回 400 + {"error": "具体错误信息"}
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)  // 专门拦截 @Valid 校验失败
    public ResponseEntity<Map<String, String>> handleValid(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(err ->
            errors.put(err.getField(), err.getDefaultMessage())
            // 遍历所有校验失败的字段,以 "字段名 → 错误信息" 存入 Map
            // 比如 {"username": "用户名不能为空", "age": "年龄不能大于150"}
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);  // 400 + 所有字段错误
    }
}

作用:项目里任何地方抛出的异常,都会被这里拦截并统一转换成 JSON 响应,避免把堆栈直接暴露给客户端。

5.8resources/mapper/UserMapper.xml(SQL 映射文件)

<?xml version="1.0" encoding="UTF-8" ?>     <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">    <!-- MyBatis Mapper DTD 约束 -->

<mapper namespace="com.example.demo4.mapper.UserMapper">
    <!-- namespace 必须等于 UserMapper 接口的全限定名,这样 MyBatis 才能把 SQL 和接口方法对应 -->

    <resultMap id="BaseResultMap" type="User">
        <!-- resultMap:把数据库列 → Java 属性 的映射关系 -->
        <id column="id" property="id"/>        <!-- <id> 表示主键列 -->
        <result column="username" property="username"/>  <!-- 普通列映射 -->
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender"/>
    </resultMap>

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        <!-- id="insert" 对应 UserMapper.insert 方法 -->
        <!-- useGeneratedKeys + keyProperty:让数据库自增主键回填到 user.id -->
        INSERT INTO user (username, name, age, gender)
        VALUES (#{username}, #{name}, #{age}, #{gender})
        <!-- #{xxx} 是 MyBatis 占位符,会从 user 对象取对应属性值 -->
    </insert>

    <update id="updateById">
        UPDATE user SET username=#{username}, name=#{name}, age=#{age}, gender=#{gender}
        WHERE id=#{id}
    </update>

    <delete id="deleteById">
        DELETE FROM user WHERE id=#{id}
    </delete>

    <select id="selectById" resultMap="BaseResultMap">
        <!-- resultMap="BaseResultMap":用上面定义的映射规则把行 → User 对象 -->
        SELECT id, username, name, age, gender FROM user WHERE id=#{id}
    </select>

    <select id="selectAll" resultMap="BaseResultMap">
        SELECT id, username, name, age, gender FROM user
    </select>
</mapper>

作用:把 SQL 语句和 Mapper 接口方法绑定。#{xxx} 是预编译占位符(防 SQL 注入)。

5.9application.properties(配置文件)

spring.datasource.url=jdbc:mysql://localhost:3306/blog_platform?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8
# 数据库连接地址:MySQL 在本地 3306,库名 blog_platform
# useSSL=false:不用 SSL
# allowPublicKeyRetrieval=true:允许 MySQL 8 的公钥检索(之前报错的修复)
# serverTimezone=Asia/Shanghai:时区上海
# characterEncoding=utf8:编码 UTF-8

spring.datasource.username=root      # MySQL 用户名
spring.datasource.password=xxxxxx    # MySQL 密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  # MySQL 8 的 JDBC 驱动类

mybatis.mapper-locations=classpath:mapper/*.xml        # 扫描 resources/mapper 下所有 XML
mybatis.type-aliases-package=com.example.demo4.entity    # 这个包下的类可以用简短别名(如 User 而非全限定名)
mybatis.configuration.map-underscore-to-camel-case=true  # user_name → userName 自动驼峰转换
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl  # 控制台打印 SQL 日志

Spring Boot主启动类启动后可以通过swagger文档来测试接口是否正确

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐