Spring Boot 从接口设计到业务编排

Spring Boot 在企业级项目中的真正价值,从来不是"自动装配"或者"快速启动"。这些只是工具层面的便利性,而项目能否真正落地,决定因素永远是:

  • 后端能否提供稳定、统一、可扩展的接口给前端调用

  • 业务逻辑是否清晰、可维护、便于扩展

  • 数据库访问是否规范、可靠、性能可控

  • 项目结构是否合理,后期迭代是否轻松

换言之,一个优秀的 Spring Boot 项目,应该是一套围绕"业务目标"而构建的工程体系。让我们一起看看一个企业级项目中,接口层、业务层、数据库层到底如何协作,如何组织代码,如何使整个项目保持多年可维护性。

目录

一、项目整体结构

[1.1 为什么分层结构如此重要?](#1.1 为什么分层结构如此重要?)

[1.2 分层的真实作用(逐层解析)](#1.2 分层的真实作用(逐层解析))

(1)Controller:负责"请求与响应"

(2)Service:负责"业务逻辑"

[(3)Repository / Mapper:负责"数据访问"](#(3)Repository / Mapper:负责“数据访问”)

[(4)DTO / Entity / VO 的作用](#(4)DTO / Entity / VO 的作用)

二、接口是如何提供给前端调用的

[2.1 REST 控制器的原理与写法](#2.1 REST 控制器的原理与写法)

[为什么要使用 @RequestMapping("/api/user")?](#为什么要使用 @RequestMapping("/api/user")?)

[为什么使用 @RequestBody?](#为什么使用 @RequestBody?)

[2.2 参数接收方式:HTTP 的三种常见模式](#2.2 参数接收方式:HTTP 的三种常见模式)

[(1)@RequestParam:用于接收 URL 参数(GET 常用)](#(1)@RequestParam:用于接收 URL 参数(GET 常用))

[(2)@RequestBody:接收前端发送的 JSON(POST 常用)](#(2)@RequestBody:接收前端发送的 JSON(POST 常用))

(3)@PathVariable:接收路径变量

[2.3 为什么需要统一返回结构?](#2.3 为什么需要统一返回结构?)

三、业务逻辑层(Service):项目的核心大脑

[3.1 为什么不能把业务写在 Controller?](#3.1 为什么不能把业务写在 Controller?)

[3.2 标准 Service 写法](#3.2 标准 Service 写法)

[3.3 参数校验的重要性](#3.3 参数校验的重要性)

[四、数据库访问层:Repository 与 Mapper 的实战写法(增强版)](#四、数据库访问层:Repository 与 Mapper 的实战写法(增强版))

[4.1 JPA:适合 CRUD 项目的轻量方案](#4.1 JPA:适合 CRUD 项目的轻量方案)

实体类映射说明

[4.2 MyBatis:适用于复杂 SQL、业务量大的场景](#4.2 MyBatis:适用于复杂 SQL、业务量大的场景)

[Mapper 接口](#Mapper 接口)

五、从接口到数据库


一、项目整体结构

实际项目开发中,最重要的不是"能跑",而是可维护、可扩展、可协作

分层架构之所以能在行业里流传几十年,就是因为它完美符合企业级项目的需求。

1.1 为什么分层结构如此重要?

一个真实项目往往会迭代数年,业务逻辑复杂度会逐渐升高,团队成员可能不断更换。

如果后端不分层,会出现以下严重问题:

  • 代码难以阅读 → 新人接手困难

  • 业务逻辑、数据库操作混在一起 → 难以测试

  • 任何修改都可能导致不可控的连锁反应

  • Controller 臃肿,Mapper 过度膨胀,Service 沦为拼接代码的中转站

因此合理的分层是后端工程质量的核心基础。


1.2 分层的真实作用(逐层解析)

(1)Controller:负责"请求与响应"

Controller 的职责非常单一:

  • 接收前端参数

  • 对参数进行初步校验

  • 调用 Service

  • 将结果转换为统一格式返回前端

注意:Controller 不允许写任何业务逻辑。


(2)Service:负责"业务逻辑"

业务逻辑包括:

  • 参数进一步校验

  • 查询数据库以判断业务条件

  • 组合多个数据源的结果

  • 执行业务流程(例如下单 → 扣库存 → 生成支付单)

  • 调用多个 Mapper/Repository

  • 实现事务逻辑

Service 层也不负责数据库操作,而是使用 Repository/Mapper 作为唯一入口。


(3)Repository / Mapper:负责"数据访问"

Repository(JPA)或 Mapper(MyBatis)是真正负责与数据库交互的部分。

这层的特点是:

  • 不写业务逻辑

  • 不做流程控制

  • 专注 SQL 或 ORM 映射

Repository/Mapper 是底层,它屏蔽了数据库细节,Service 不需要关注 SQL 语句本身。


(4)DTO / Entity / VO 的作用

前后端传输的数据实际上经历三个阶段:

类型 作用 来源/去向
DTO Controller 接收前端参数 来自前端
Entity 表结构映射 用于数据库存储
VO 返回前端展示的数据 给前端

这三个对象的分工明确,可以有效避免前端参数直接影响数据库结构。

二、接口是如何提供给前端调用的

在现代 Web 开发中,前后端通常通过 REST API 通信。Spring Boot 默认支持基于 HTTP + JSON 的接口风格,因此只需编写简单的注解即可完成路由映射。

2.1 REST 控制器的原理与写法

一个 Controller 本质上是:

  • Spring Boot 启动时自动扫描的 Bean

  • 每个方法都能映射到一个 HTTP 路由

  • 负责处理 JSON 输入、返回 JSON 输出

    @RestController
    @RequestMapping("/api/user")
    public class UserController {

    复制代码
      @Autowired
      private UserService userService;
    
      @PostMapping("/create")
      public Result<UserVO> createUser(@RequestBody UserDTO dto) {
          return Result.success(userService.createUser(dto));
      }

    }

为什么要使用 @RequestMapping("/api/user")

  • 便于管理同一业务域的接口

  • 避免路由冲突

  • 更符合 REST 的语义化设计

为什么使用 @RequestBody

  • 接收 JSON 格式参数

  • 可映射为 DTO 类

  • 便于字段校验

2.2 参数接收方式:HTTP 的三种常见模式

Spring Boot 完整支持 Web API 中的所有参数类型。

(1)@RequestParam:用于接收 URL 参数(GET 常用)

复制代码
GET /api/user/detail?id=1

接收

复制代码
@GetMapping("/detail")
public Result<UserVO> detail(@RequestParam Long id) {
    return Result.success(userService.detail(id));
}

(2)@RequestBody:接收前端发送的 JSON(POST 常用)

复制代码
{
  "name": "Alice",
  "email": "alice@example.com"
}

接收

复制代码
@PostMapping("/update")
public Result<Boolean> update(@RequestBody UserDTO dto) {
    return Result.success(userService.update(dto));
}

(3)@PathVariable:接收路径变量

复制代码
GET /api/user/11

接收

复制代码
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
    return Result.success(userService.detail(id));
}

2.3 为什么需要统一返回结构?

在企业开发中,不可能让每个 Controller 自己决定响应格式,这会导致前端无法稳定解析数据。

统一响应结构的好处包括:

  • 前端解析更简单,只需判断 code == 0

  • 后端接口风格一致

  • 异常情况统一处理

  • 接口文档更加规范

通用结构如下:

复制代码
{
  "code": 0,
  "msg": "success",
  "data": { ... }
}

使用 Result 包装类即可实现。

三、业务逻辑层(Service):项目的核心大脑

Service 层是整个项目的业务中心,它负责执行流程、编排逻辑、做条件判断、验证参数、决定是否允许数据写入数据库。

3.1 为什么不能把业务写在 Controller?

虽然 Controller 写业务"看起来更快",但会导致:

  • Controller 越写越大,无法维护

  • 无法复用逻辑

  • 无法进行单元测试

  • 无法进行事务控制(Service 才允许)

  • 系统结构失去扩展性

因此,企业级项目中统一要求:
Controller 只能做"输入输出",其余全部在 Service。


3.2 标准 Service 写法

业务接口:

复制代码
public interface UserService {
    UserVO createUser(UserDTO dto);
    UserVO detail(Long id);
    Boolean update(UserDTO dto);
}

业务实现:

复制代码
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserVO createUser(UserDTO dto) {

        // 1. 校验邮箱是否重复
        if (userRepository.findByEmail(dto.getEmail()).isPresent()) {
            throw new BusinessException("邮箱已存在,无法重复注册");
        }

        // 2. 创建实体对象
        UserEntity entity = new UserEntity();
        entity.setName(dto.getName());
        entity.setEmail(dto.getEmail());
        entity.setCreateTime(LocalDateTime.now());

        // 3. 写入数据库
        userRepository.save(entity);

        // 4. 返回 VO
        return new UserVO(entity.getId(), entity.getName(), entity.getEmail());
    }
}

这段代码体现了企业开发中常见的业务处理方式:

  1. 校验参数

  2. 判断状态(例如是否重复、是否满足业务条件)

  3. 进行业务逻辑组合

  4. 操作数据库

  5. 组装返回结果

3.3 参数校验的重要性

后端需要保证数据的可靠性,例如:

复制代码
public class UserDTO {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @Email(message = "邮箱格式不正确")
    private String email;
}

在 Controller 中使用:

复制代码
public Result<UserVO> createUser(@Valid @RequestBody UserDTO dto)

这样读者只需关注业务逻辑,参数合法性交给 Spring 校验。

四、数据库访问层:Repository 与 Mapper 的实战写法(增强版)

Spring Boot 默认支持两种主流方式:

  • JPA(快速自动 SQL)

  • MyBatis(精细控制 SQL)

项目中常按业务复杂度选择。


4.1 JPA:适合 CRUD 项目的轻量方案

JPA 的优点:

  • 无需写 SQL

  • 使用实体对象操作数据库

  • 支持分页、排序、动态查询

  • 更符合"面向对象"的编程方式

实体类映射说明

复制代码
@Entity
@Table(name = "t_user")
@Data
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    private LocalDateTime createTime;
}

说明:

  • @Entity:映射成数据库表

  • @Table(name = "t_user"):指定表名

  • @Id:主键字段

  • @GeneratedValue:自增主键策略

4.2 MyBatis:适用于复杂 SQL、业务量大的场景

MyBatis 的优点:

  • SQL 完全可控

  • 性能更可优化

  • 复杂查询使用 XML 更灵活

  • 大型项目、大数据场景首选

Mapper 接口

复制代码
@Mapper
public interface UserMapper {
    UserEntity selectByEmail(String email);
}

XML 定义 SQL

复制代码
<select id="selectByEmail" resultType="UserEntity">
    SELECT * FROM t_user WHERE email = #{email}
</select>

MyBatis 方式更接近传统关系型数据库使用方式,可精准控制执行效率与查询结构。

五、从接口到数据库

从前端请求到数据库写入的完整路径:

复制代码
前端发送 JSON 请求
   ↓
Controller 接收 DTO、校验、调用 Service
   ↓
Service 执行业务判断、校验逻辑、组织流程
   ↓
Repository / Mapper 发起数据库操作
   ↓
数据库写入或查询
   ↓
Service 组装 VO
   ↓
Controller 返回 Result<T>
   ↓
前端收到统一格式的响应
相关推荐
程序员爱钓鱼40 分钟前
Node.js 编程实战:文件系统(fs)模块详解
后端·node.js·trae
Q_Q51100828542 分钟前
python+django/flask+vue的高考志愿咨询系统
spring boot·python·django·flask·node.js·php
Q_Q5110082851 小时前
python+django/flask+vue校园闲置物品交易系统
spring boot·python·django·flask·node.js
yihuiComeOn1 小时前
【大数据高并发核心场景实战】 - 设计秒杀架构必知必会的那些事
java·后端·微服务·架构
Q_Q5110082851 小时前
python+django/flask+vue基于web的产品管理系统
前端·spring boot·python·django·flask·node.js
Q_Q19632884751 小时前
python+django/flask+vue的电子产品销售系统
spring boot·python·django·flask·node.js·php
Q_Q5110082851 小时前
python+django/flask+vue的小型房屋租赁系统
spring boot·python·django·flask·node.js·php
iナナ1 小时前
Java自定义协议的发布订阅式消息队列(一)
java·开发语言·spring·消息队列·生成消费者模型
一直都在5721 小时前
手写tomcat(2):Servlet原理和自定义tomcat
java·servlet·tomcat