springboot 技术学习 03 项目最小mvp

📅 2026/7/19 0:53:57 👁️ 阅读次数 📝 编程学习
springboot 技术学习 03 项目最小mvp

 

 

 

这套极其经典的依赖组合(Spring Boot + MyBatis-Plus + Druid + Lombok + Fastjson),实现一个最短、最少代码、但架构合理、层次分明的 MVP(最小可行性产品),我们可以遵循 Spring Boot 的经典三层架构来进行切分。

为了保证“合理分开代码”的同时做到“最短最少”,我们不写任何一行多余的业务逻辑,仅以一个最核心的“员工管理(Employee)”为例。

1. 怎么切分代码(最合理的最小项目结构)

src/main/java/com/example 下建立以下 5 个核心包,职责绝不交叉:

Plaintext
 
com.example│─ App.java                  (启动类:项目入口)├─ entity                    (实体层:对应数据库表,纯数据结构)│   └─ Employee.java├─ mapper                    (数据访问层:MyBatis-Plus 的核心,负责 SQL)│   └─ EmployeeMapper.java├─ service                   (业务逻辑层:处理核心业务,这里用 MP 的通用 Service)│   ├─ EmployeeService.java│   └─ impl/EmployeeServiceImpl.java└─ controller               (控制层:暴露 HTTP 接口,接收请求、返回 Fastjson 结果)└─ EmployeeController.java

2. 最短、最少的代码实现

1. 实体层 (Entity)

利用 Lombok 消除所有 Getter/Setter,利用 MyBatis-Plus 消除所有 CRUD 的 SQL 映射。

Java
 
package com.example.entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("employee") // 映射数据库表
public class Employee {@TableId // 主键private Long id;private String username;private String password;private String name;private Integer status;
}

 

2. 数据访问层 (Mapper)

继承 MyBatis-Plus 的 BaseMapper不需要写任何代码和 XML,就已经自动拥有了增删改查、分页的能力。

Java
 
package com.example.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Employee;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {// 零代码,自动继承所有单表增删改查方法
}

 

3. 业务逻辑层 (Service)

利用 MyBatis-Plus 的通用 Service 接口和实现类,同样可以用零行具体代码实现全套业务组合。

  • 接口 (Service):

Java
 
package com.example.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.example.entity.Employee;public interface EmployeeService extends IService<Employee> {
}

 

  • 实现类 (ServiceImpl):

Java
 
package com.example.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.Employee;
import com.example.mapper.EmployeeMapper;
import com.example.service.EmployeeService;
import org.springframework.stereotype.Service;@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {// 零代码,自动获得 MP 强大的批量操作和高级业务方法
}

 

4. 控制层 (Controller)

这里利用了 Fastjson 进行数据的序列化/反序列化(在请求体中自动解析,并在返回时转换为 JSON)。同时可以用 commons-lang (StringUtils) 来做一行极简的参数校验。

Java
 
package com.example.controller;import com.alibaba.fastjson.JSON;
import com.example.entity.Employee;
import com.example.service.EmployeeService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/employee")
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;// 1. 新增员工(使用 Fastjson 接收和打印)
    @PostMappingpublic String save(@RequestBody String jsonStr) {// 使用 Fastjson 解析前端传来的复杂 JSONEmployee employee = JSON.parseObject(jsonStr, Employee.class);// 使用 commons-lang 极简校验if (StringUtils.isBlank(employee.getUsername())) {return "用户名不能为空";}employeeService.save(employee);return "新增成功";}// 2. 根据ID查询员工@GetMapping("/{id}")public String getById(@PathVariable Long id) {Employee employee = employeeService.getById(id);// 使用 Fastjson 将对象序列化为标准 JSON 字符串返回return JSON.toJSONString(employee);}
}

 

5. 启动类 (App)

Java
 
package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}

 

3. 配置文件怎么写最少?(application.yml)

在这里把 mysql-connector-javadruid-spring-boot-starter 连接池融为一体:

YAML
 
spring:datasource:druid: # 声明使用 Druid 连接池url: jdbc:mysql://localhost:3306/takeout?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8username: rootpassword: yourpassworddriver-class-name: com.mysql.cj.jdbc.Driver

💡 为什么说这是最合理、最少代码的 MVP?

  1. 依赖分工明确:

    • spring-boot-starter-web 负责暴露 HTTP 路由。

    • lombok 删掉了几十行的 Getter/Setter 和构造函数。

    • mybatis-plus 彻底消灭了 Mapper.xml 和 Service 里的样板 CRUD 代码。

    • druid + mysql-connector 默默在底层管理数据库连接。

    • fastjsoncommons-lang 在 Controller 层协同完成了最快速的“数据解析 + 字符串校验”。

  2. 结构严格分离: 没有任何一层跨职能工作。Controller 只管接发信号,Service 只管业务,Mapper 只管数据。虽然总代码量加起来不过几十行,但它完全具备了向大型企业级项目扩容的骨架。

 

 

 

 

1. 每一层的数据交互流程(请求的“奇幻旅险”)

当用户在手机上点击一个按钮(比如“点外卖”或“登录”),数据在这一套分层架构中是像接力赛一样流转的。

我们以“大堂经理(后台)新增一个菜品”为例,看看数据(Data)是如何在各层之间传递和演变的:

Plaintext
 
 📱 前端 (浏览器/微信小程序) │ ▼ 【1. 发出 JSON 数据】: {"name":"宫保鸡丁", "price":25.0, "categoryId":1}──Controller 层 (控制层)───────────────────────────────────────────────│ 职责:迎宾、安检、传话。│ 1. 收到 JSON 字符串。│ 2. 用 Fastjson 把 JSON 翻译成 Java 的 Dish 对象。│ 3. 用 commons-lang 检查菜名是不是空的。▼ 【2. 传递 Dish 对象】──Service 层 (业务逻辑层)─────────────────────────────────────────────│ 职责:核心业务逻辑、出谋划策。│ 1. 触发“06-自动填充”:偷偷帮这个菜加上创建时间、创建人。│ 2. 检查这个分类下菜品有没有超过上限(如果有这种业务的话)。▼ 【3. 传递带有完整属性的 Dish 对象】──Mapper 层 (数据访问层)──────────────────────────────────────────────│ 职责:翻译官、数据库打交道。│ 1. MyBatis-Plus 把 Dish 对象自动翻译成 SQL 语句:│    "INSERT INTO dish (name, price) VALUES ('宫保鸡丁', 25.0);"▼ 【4. 发送 SQL 指令】(通过 Druid 连接池和 MySQL 驱动发给数据库)💾 MySQL 数据库 ─── 成功写入一行数据!

2. 图片上的功能,应该分别放在哪一层?

我们把你在前几张图里看到的 16个基础功能9个进阶功能,按照业界最标准的规范进行“对号入座”:

🟢 Controller 层(控制层):负责接口的暴露和接入

只要是直接面对前端、处理 HTTP 请求、管卡口、管输入输出的功能,都写在这里。

  • 02-后台功能 / 03-完善登录:接收账号密码,校验成功后发放或验证 JWT 令牌。

  • 04/10/12-分页查询:接收前端传来的 page(第几页)和 pageSize(每页几条)参数。

  • 08-上传&下载:接收前端传过来的 MultipartFile 文件,并调用云存储将其变成一个图片 URL。

  • 13-短信&验证码:接收手机号,调用短信网关发送,并将验证码存入 Redis。

🔵 Service 层(业务逻辑层):负责核心“商业大脑”

只要涉及到多张表的操作、业务规则校验、状态改变、缓存利用,一律是 Service 层的核心职责。

  • 03-新增员工 / 07-新增分类 / 09-新增菜品 / 11-新增套餐:检查数据是否合法,并在写入前做业务处理。

  • 05-启用&禁用账号:去数据库查出这个员工,把状态改为 0(禁用)或 1(启用),然后再保存。

  • 15-购物车 / 16-用户下单

    • 下单是全项目最复杂的业务:要先查地址簿(14),再查购物车(15),然后算总价,接着往订单表插入一条,往订单明细表插入多条,最后把购物车清空。这种多表联动、成败在此一举的逻辑,必须写在 Service 层,并加上 @Transactional(事务标签)。

  • 进阶 01/02/03-缓存功能:使用 @Cacheable(Spring Cache)标签。当用户查菜品列表(14)时,Service 优先去 Redis(缓存)里抓取,抓不到才去翻数据库。

🟡 Mapper 层(数据访问层)+ 实体层:纯粹的数据库映射

因为用了 MyBatis-Plus,这一层代码极少,但它在背后默默支撑所有底层的数据库变动。

  • 06-自动填充:通过编写一个 MyBatis-Plus 的 MetaObjectHandler 配置类。它在 Mapper 层执行 insertupdate 的瞬间触发,自动给实体的 createTime 等字段塞入当前时间。

  • 进阶 04/05-MySQL主从复制与读写分离:通过配置多数据源(比如 Dynamic DataSource)。当 Mapper 执行 select 开头的语句时,自动把请求引向“从库”;执行 insert/update 时,引向“主库”。

🟤 项目外部/运维层:不写在 Java 代码里

  • 进阶 06/07-Nginx命令与应用:独立安装在 Linux 服务器上的软件,负责把前端静态网页展示给用户,并把后端的 /api 请求转发给你的 Spring Boot 项目。

  • 进阶 08-前后端分离开发:前端写前端的 Vue/微信小程序,后端写后端的 Java,双方通过 Swagger 接口文档和 JSON 数据进行对接。

  • 进阶 09-项目部署:把写好的代码用 maven-plugin 打包成 jar 包,丢到 Linux 服务器上运行。

 

 

 

 

 

 

 

 

 

 

 

是的,这里无处不在地用到了控制反转(IoC)和依赖注入(DI)!可以说,如果没有这两个核心概念,上面的代码根本跑不起来。

简单来说:

  • 控制反转(IoC - Inversion of Control): 以前我们要用一个对象,得自己 new。现在我们不自己 new 了,把创建对象的“控制权”翻转交给 Spring 容器(大管家)。大管家启动时,会自动把这些对象创建好,放在一个“大篮子”里。

  • 依赖注入(DI - Dependency Injection): 当 A 对象需要用到 B 对象时,不用自己去满世界找,大管家会自动把 B 对象“塞进”(注入) A 对象里面。

那些带着 @ 符号的统称为注解(Annotation),它们就是你给 Spring 大管家下的“秘密指令”。每一个 @ 都有独特的魔法:

1. 告诉大管家:“把这个类实例化,放进你的大篮子里”

为了让 Spring 帮你管理对象,你必须在类名上加以下注解,大管家看到后就会自动 new 它:

  • @SpringBootApplication (在 App.java 上)

    • 大白话:这是项目的总指挥官。它告诉 Spring:“我是整个项目的入口,请从我所在的这个包开始,满世界去搜寻带有 @RestController@Service@Mapper 的类,把它们通通 new 出来管理起来!”

  • @RestController (在 EmployeeController 上)

    • 大白话:告诉大管家:“我是一个前台接待员(控制器)。请帮我 new 出来,并且帮我连上网络。只要有前端的 HTTP 请求过来,就交给我来处理。另外,我嘴里吐出来的话(返回值),一律自动用 Fastjson 转换成 JSON 字符串发给前端。”

  • @Service (在 EmployeeServiceImpl 上)

    • 大白话:告诉大管家:“我是一个后厨厨师长(业务逻辑核心)。请帮我 new 出来放到篮子里,等会儿前台接待员(Controller)要点菜时会用到我。”

  • @Mapper (在 EmployeeMapper 上)

    • 大白话:告诉大管家:“我是一个账本抄写员(数据访问层)。虽然我只是个接口,连大括号 {} 都没有,但因为我继承了 MyBatis-Plus,请用魔法(动态代理)帮我生成一个实现类,并放进篮子里。等后厨需要查数据库时来找我。”

2. 告诉大管家:“我需要那个对象,请把它注入给我”

  • @Autowired (在 Controller 里面调用 Service 时)

    • 大白话:这就是依赖注入(DI)的施法现场!

    • EmployeeController 里面,我们写了这一行:

      Java
       
      @Autowired
      private EmployeeService employeeService;
      

      这里我们没有写 = new EmployeeServiceImpl()。Controller 只要往大管家面前一站,头上顶着 @Autowired,大管家就会心领神会,立刻去自己的“大篮子”里把刚刚 new 好的 @Service(厨师长)给塞到这个变量里。这就是依赖注入

3. 路由与传参指令(前台接待处的精细分工)

  • @RequestMapping("/employee")

    • 大白话:划分地盘。告诉大管家:“只要是访问 http://.../employee 这个网址的请求,统统带路到我这个类来。”

  • @PostMapping / @GetMapping

    • 大白话:精准对接动作。如果是新增数据(POST 动作),就由 @PostMapping 的方法接待;如果是查询数据(GET 动作),就由 @GetMapping 的方法接待。

  • @RequestBody

    • 大白话:拆包裹。前端发过来的数据是打包在 HTTP 请求体(Body)里的 JSON 字符串。大管家看到这个注解,就会启动 Fastjson,把这堆字符串“拆包”并翻译成 Java 认识的对象或纯字符串。

  • @PathVariable

    • 大白话:顺手牵羊。如果网址写的是 /employee/123,加了 @PathVariable Long id,大管家就会自动把网址最后那个 123 抠出来,赋值给你的 id 变量。

4. 其他小工具指令

  • @Data (Lombok 提供,在实体类上)

    • 大白话:偷懒神器。写代码时虽然看不见,但编译成 class 文件时,Lombok 看到它就会自动帮这个类生成全套的 Getter/Setter/toString

  • @TableName("employee") (MyBatis-Plus 提供)

    • 大白话:对暗号。告诉 MyBatis-Plus:“这个 Java 类对应的数据库表名叫 employee,等会儿你自动生成 SQL 语句的时候别写错了。”

总结数据流转中的 IoC 与 DI 关系:

  1. 项目启动(@SpringBootApplication) ➔

  2. 大管家自动 newMapperServiceController(IoC 控制反转) ➔

  3. 大管家发现 Controller 缺了 Service,于是用 @Autowired 把 Service 塞了进去(DI 依赖注入) ➔

  4. 顾客发送请求 ➔ Controller 拿着被注入进来的 Service 顺畅地调用业务逻辑。