Spring Boot 项目开发全流程实战指南

Spring Boot 项目开发全流程实战指南:从 0 到 1 实现 CURD(含面试题)

📅 发布时间 :2026-01-11

🏷️ 标签 :Java, Spring Boot, 教程, 后端开发, RESTful

💡 摘要:本文专为 Java 初学者量身打造,手把手带你理解 Spring Boot 项目的标准开发流程。从 Entity 实体类定义,到 Controller 接口开放,全链路打通。包含超详细的代码注释、核心概念图解以及高频面试题。


📖 一、 引言:Spring Boot 开发在做什么?

很多新手在刚接触 Spring Boot 时,会被各种层级搞晕:Controller, Service, Mapper, Entity... 到底先写谁?谁调谁?

其实,Web 开发的核心流程就像餐厅的点餐流程:

  1. Controller (服务员):直接面对客人(前端),拿着菜单(接口文档),记录客人的需求(接收请求参数),把菜端给客人(返回响应数据)。
  2. Service (大厨):负责核心烹饪(业务逻辑)。比如把肉切好、炒熟。如果发现菜没了(异常),就告诉服务员。
  3. Mapper/Repository (采购员):只负责去仓库(数据库)拿原材料(增删改查数据),不负责烹饪。
  4. Entity (食材):就是在各个环节传递的数据对象(比如红烧肉)。

今天我们就来演示如何制作一道 "用户管理" 的菜。


🏗️ 二、 项目结构与开发顺序

一个标准的 Spring Boot 项目,通常包含以下层级(建议按此顺序编写):

  1. 📄 Entity (实体层):定义数据库表结构对应的 Java 类。
  2. 💾 Mapper/DAO (持久层):操作数据库的接口(MyBatis 或 JPA)。
  3. 🧠 Service (业务层):编写复杂的业务逻辑。
  4. 🌐 Controller (控制层):对外暴露 URL 接口。

💻 三、 代码实战:实现用户增删改查

我们将实现一个简单的用户管理功能。

3.1 第一步:Entity (食材准备)

实体类是数据的载体,直接对应数据库中的表。我们分两步来写:

1. 类定义与主键配置

首先定义类的主体结构,并配置好主键生成策略。

java 复制代码
package com.example.demo.entity;

import lombok.Data; // Lombok 插件,自动生成 getter/setter/toString
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

/**
 * 用户实体类
 * 对应数据库表:t_user
 */
@Data // Lombok 注解:自动生成 Getter, Setter, ToString, HashCode 等方法,省去手动编写
@Entity // JPA 注解:声明这是一个实体类,与数据库表映射
@Table(name = "t_user") // 指定对应的数据库表名为 t_user
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键 ID
     * @Id: 标识该属性为主键
     * @GeneratedValue: 指定主键生成策略,IDENTITY 表示使用数据库自增 (Auto Increment)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    // ... 下面继续添加其他字段

2. 业务字段定义

接下来,在 User 类中补充用户名、密码等业务字段。

java 复制代码
    /**
     * 用户名
     * @Column: 映射数据库字段属性
     * nullable = false: 数据库这一列不能为空
     * unique = true: 用户名必须唯一
     */
    @Column(nullable = false, unique = true, length = 50)
    private String username;

    /**
     * 密码 (实际开发中不能存明文,要存加密后的哈希值)
     */
    @Column(nullable = false, length = 100)
    private String password;

    /**
     * 邮箱
     */
    @Column(length = 100)
    private String email;

    /**
     * 创建时间
     * 用于记录这一条数据是什么时候插入生成的
     */
    @Column(name = "create_time")
    private Date createTime;
}

3.2 第二步:Mapper/Repository (仓库采购)

这里演示使用 Spring Data JPA,它非常强大,只需继承 JpaRepository 接口,基本的增删改查代码都不用自己写!

java 复制代码
package com.example.demo.repository;

import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * 用户持久层接口
 * 继承 JpaRepository<实体类类型, 主键类型>
 * 
 * 作用:这就是那个"采购员",Spring Boot 自动帮你实现好了去数据库拿数据的方法:
 * - save(User user): 保存或更新
 * - findById(Long id): 根据ID查询
 * - findAll(): 查询所有
 * - deleteById(Long id): 根据ID删除
 */
@Repository // 标记这是持久层组件,Spring 会把它放入容器管理
public interface UserRepository extends JpaRepository<User, Long> {

    /**
     * 自定义查询方法
     * JPA 的神奇之处:只要按照规则命名方法,SQL 语句自动生成!
     * 
     * 翻译:select * from t_user where username = ?
     */
    User findByUsername(String username);
    
    /**
     * 翻译:select * from t_user where email = ?
     */
    User findByEmail(String email);
}

3.3 第三步:Service (大厨烹饪)

业务逻辑都在这里。我们将代码拆分为两个部分:基础注入和核心业务逻辑。

1. 依赖注入与类结构

首先,我们需要把 UserRepository (采购员) 注入进来,方便后续调用。

java 复制代码
package com.example.demo.service;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

@Service // 标记这是业务层组件
public class UserService {

    @Autowired // 依赖注入:告诉 Spring,我要用这个采购员(Repository),请帮我送进来
    private UserRepository userRepository;

2. 核心业务:注册逻辑

这是最复杂的逻辑,包含参数校验、重名检查和数据补全。

java 复制代码
    /**
     * 新增用户 (注册)
     * 业务逻辑:
     * 1. 检查用户名是否存在
     * 2. 补全创建时间
     * 3. 保存到数据库
     */
    @Transactional // 开启事务:保证一系列操作要么全成功,要么全失败
    public User register(User userData) {
        // 1. 简单校验
        if (userData.getUsername() == null) {
            throw new RuntimeException("用户名不能为空");
        }

        // 2. 检查是不是重名了
        User existUser = userRepository.findByUsername(userData.getUsername());
        if (existUser != null) {
            throw new RuntimeException("用户名已存在,换一个吧");
        }

        // 3. 补全系统字段 (创建时间)
        userData.setCreateTime(new Date());

        // 4. 调用持久层保存
        return userRepository.save(userData);
    }

3. 其他业务逻辑

查询和删除相对简单,直接调用 Repository 的方法即可。

java 复制代码
    /**
     * 查询所有用户列表
     */
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }

    /**
     * 根据 ID 删除用户
     */
    @Transactional
    public void deleteUser(Long id) {
        if (!userRepository.existsById(id)) {
            throw new RuntimeException("要删除的用户不存在!");
        }
        userRepository.deleteById(id);
    }
}

3.4 第四步:Controller (服务员接客)

这是对外的窗口,通常遵循 RESTful 风格设计。为了清晰,我们按功能模块拆分代码。

1. 控制器初始化

定义 API 的基础路径 /api/users,并注入 Service。

java 复制代码
package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 用户管理控制器
 * @RestController = @Controller + @ResponseBody
 */
@RestController // 声明这是一个 REST 风格的控制器
@RequestMapping("/api/users") // 定义统一的基础路径
public class UserController {

    @Autowired
    private UserService userService; // 注入大厨 (Service)

2. 查询接口 (GET)

对应 HTTP GET 请求,用于获取资源。

java 复制代码
    /**
     * 获取用户列表
     * URL: GET /api/users
     */
    @GetMapping 
    public List<User> list() {
        return userService.findAllUsers();
    }

3. 新增接口 (POST)

对应 HTTP POST 请求,用于创建资源。注意 @RequestBody 的使用,它负责接收 JSON 数据。

java 复制代码
    /**
     * 注册新用户
     * URL: POST /api/users
     * @RequestBody: 读取请求体中的 JSON 数据转为 User 对象
     */
    @PostMapping
    public String add(@RequestBody User user) {
        try {
            userService.register(user);
            return "注册成功!"; 
        } catch (Exception e) {
            return "注册失败: " + e.getMessage();
        }
    }

4. 删除接口 (DELETE)

对应 HTTP DELETE 请求。注意 @PathVariable 用于从 URL 中提取 ID。

java 复制代码
    /**
     * 删除用户
     * URL: DELETE /api/users/{id} (例如 /api/users/5)
     * @PathVariable: 从 URL 路径中提取 {id} 的值
     */
    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        try {
            userService.deleteUser(id);
            return "删除成功";
        } catch (Exception e) {
            return "删除失败: " + e.getMessage();
        }
    }
}

🧐 四、 核心概念名词解释(小白必看)

1. IoC (控制反转) 与 DI (依赖注入)

  • IoC :以前我们需要对象,必须自己 new UserService()。现在我们把创建对象的权利交给 Spring 容器(管家)。
  • DI :我们需要用对象时,在属性上加个 @Autowired,Spring 就会帮我们把创建好的对象注入进来。

2. Spring Bean

  • 被 Spring 容器管理的对象就叫 Bean。
  • 只要加上 @Controller, @Service, @Repository, @Component 这些注解,类就会变成 Bean。

3. JSON

  • 前后端交互的"普通话"。Controller 返回的 Java 对象会被自动转换成 JSON 字符串格式(比如 {"name": "张三", "age": 18}),前端 JS 能轻松看懂。

🙋‍♂️ 五、 高频面试题 QA

Q1:@RestController@Controller 有什么区别?

A

  • @Controller:通常用于传统的 Web 开发,方法默认返回的是视图名字 (比如 index.html 的文件名),用于跳转页面。如果要返回 JSON 数据,需要在方法上额外加 @ResponseBody
  • @RestController:是 @Controller@ResponseBody 的组合注解。用它标记的类,所有方法默认都返回 数据 (JSON/XML),主要用于前后端分离开发。

Q2:Post 请求和 Get 请求的区别?(RESTful 视角)

A

  • GET :用于获取资源。它是幂等的(查一次和查很多次结果一样),参数拼接在 URL 后面,不安全且长度有限。
  • POST :用于新建资源(比如提交表单)。参数放在 Request Body 中,相对安全且无大小限制。

Q3:Service 层和 Controller 层的区别?代码写在一个层行不行?

A

  • 从技术上讲,所有代码写在 Controller 甚至直接写在 JSP 里都能跑,但这不仅难以维护,也没法复用。
  • Controller :负责接收请求参数校验
  • Service :负责业务逻辑(比如转账计算、逻辑判断)。这样如果其他 Controller 或者定时任务也需要用到这个逻辑,直接注入 Service 即可,实现了复用。

Q4:@Autowired@Resource 都可以注入,有什么区别?

A

  • @Autowired:是 Spring 提供的注解。默认按类型 (Type) 装配(只要你是 UserService 类我就注入)。
  • @Resource:是 JDK (Java标准) 提供的注解。默认按名称 (Name) 装配(找 ID 叫 userService 的 bean)。

🎯 六、 总结

Spring Boot 开发其实就是一场流水线作业:

  1. 定义实体:确定数据长什么样。
  2. 写 Repository:解决怎么存取数据。
  3. 写 Service:处理复杂的业务规则。
  4. 写 Controller:把功能暴露出去给别人用。

掌握了这个套路,90% 的后端业务开发你都能上手了!如果觉得文章对你有帮助,欢迎点赞收藏! 🚀


相关推荐
葫芦和十三7 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp8 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑8 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯9 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan11 小时前
多Agent之间的区别
后端
青石路13 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充13 小时前
1.面向对象设计思想
后端
IT_陈寒14 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro14 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗14 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端