1. 三层设计模式到底在解决什么问题
1.1 没有三层的时候会怎样
我带过不少刚入行的新人,问他们“写一个用户增删查改需要几步”,大多数人的第一反应是:Controller里面写个方法,方法里直接拼SQL或者用MyBatis封装好的方法一把梭。等真正接手项目的时候会发现,这种“一把梭”的代码,上线第一个月还好,第二个月开始加需求,第三个月改BUG,第四个月就没人敢动了。
为什么?因为所有逻辑都揉在一起的时候,任何一个改动都可能引发连锁反应。比如你要改一个密码校验规则,表面上看只是改一个接口,但实际上这个接口里还掺杂着数据库字段的映射、SQL语句的拼接、日志记录、异常处理、权限判断,你根本说不清楚自己改的这行代码到底影响了什么。
说个最直观的例子。我用一个小餐馆来类比:后厨、收银、采购如果是一个人,小摊子确实能养活自己;但一旦客人多起来,一个人既要做菜又要算账还要进货,必然出乱子。代码也是一样,用户量小、逻辑简单的时候,全部写在一个类里确实“能跑”,但代码是给团队协作的,不是给你一个人自嗨的。你写完三个月后回来看代码,连自己都想不起来当时为什么这么写。
1.2 三层的职责边界与调用关系
三层设计模式,或者说三层架构,核心就八个字:各司其职,单向依赖。具体到Springboot项目里,通常就是这三层:
- Controller层(表现层):负责接收HTTP请求、解析参数、做基础参数校验、调用Service层、把结果封装成统一格式返回给前端。它不关心数据从哪里来、SQL怎么写。
- Service层(业务层):负责真正的业务逻辑。比如用户新增时要检查用户名是否重复、删除时要不要做关联数据的清理、更新时哪些字段允许为空。事务边界通常也画在这一层。
- Mapper层(数据访问层):负责和数据库打交道。最简单的CRUD就是写SQL、执行SQL、返回结果。它不应该包含任何业务判断,只提供数据读写的“原子操作”。
调用关系是固定的单向链路:Controller调用Service,Service调用Mapper,数据再原路返回。不允许反向调用,也不允许跨层调用,Service不能直接去写Controller的代码,Mapper也不能跳出来处理业务逻辑。
我见过的很多“看起来很乱”的项目,问题就出在边界模糊:有人在Controller里写业务判断,有人在Service里封装SQL查询条件,还有人直接在Mapper里写业务循环。规矩一旦打破,代码就会慢慢腐化。所以三层架构的价值不是多写几个类文件,而是用结构约束住整个团队的编码习惯。
1.3 为什么用Springboot做三层特别顺
其实三层架构不是Springboot发明的,在SSH、SSM时代就已经是主流了。但Springboot把三层架构的落地难度降到了“几乎为零”,这才是它最牛的地方。
在XML配置满天飞的老项目里,你要定义一个Service,得先去XML里注册Bean,再配扫描路径,还要处理各种依赖关系。Springboot的约定大于配置,让这些事全部自动化了。你用@RestController、@Service、@Mapper三个注解就能把三层定义清楚,容器启动的时候自动扫描、自动装配,依赖注入用@Autowired就完事了,完全不需要手动new。正因为这套机制太顺滑,新手反而容易忽略三层背后的设计思想——而懂设计思想的人,写出来的代码才是真正“能懂”的代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与项目初始化
2.1 版本选型怎么避坑
先聊一个很多新手栽过跟头的问题:Springboot版本选太高了。网上搜“springboot版本太高”能搜出一堆提问,这不是个例。Springboot 3.x之后,整个生态做了非常大的调整,其中最让人头疼的就是javax.*包名全部换成了jakarta.*。很多老教程里的import javax.servlet、javax.validation在Springboot 3.x里直接编译报错,MyBatis等第三方框架如果不升级到配套版本也会闹脾气。
我的建议很明确:如果你是刚学Springboot的新手,优先用Springboot 2.7.x + JDK8。这是我个人实测下来最稳的组合,网上教程覆盖度也最高,遇到问题一搜就有答案。Springboot 2.7是2.x系列的最终版本,既有长期维护,又不会动不动就整出编译错误。
具体版本对照表我整理一下:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 8 | 企业项目最常用的版本,生态兼容性最好 |
| Springboot | 2.7.x | 2.x系列最后一个稳定版本 |
| MyBatis Starter | 2.3.x | 配合Springboot 2.x使用,无需额外设置 |
| MySQL | 8.0+ | 注意serverTimezone时区配置 |
| Maven | 3.8+ | IDEA自带也行,用命令行建议单独装 |
2.2 搭建工程与核心依赖
我用IDEA的Spring Initializr创建项目,选好版本直接生成。这里给出一份可以照抄的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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>user-crud-demo</artifactId>
<version>1.0.0</version>
<name>user-crud-demo</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
