很多刚学后端的朋友,写代码只会照搬模板,完全搞不懂 Entity、Mapper、Service、Controller 四层架构各自该干什么,代码写得混乱、逻辑堆砌,出了bug也完全不知道去哪排查。
其实后端开发的四层架构,分工特别明确,每层都有固定的职责,互不越界。今天就用最通俗的大白话+完整实战代码,带大家从零写完一个完整的查询接口,搞懂后端接口的全部流程,新手也能一次性看懂。
一、后端四层架构核心分工(重中之重)
我们可以把整个后端项目比作一家门店,四层架构对应门店的四个岗位,各司其职,这也是JavaWeb、SSM、SpringBoot项目的核心规范。
1. Entity 实体类(数据容器)
核心作用:专门对应数据库数据表,用来封装数据库查询出来的所有数据,只负责装数据,不做任何逻辑处理。
简单说:数据库一张表,对应一个Entity实体类;表里一个字段,对应类里一个属性,一一严格匹配。
专属规则
✅ 只做:封装、存储数据库数据
❌ 不做:逻辑判断、操作数据库、接收前端参数
实战示例
假设数据库有一张 user 用户表,包含字段:id、name、age、phone,对应的实体类如下:
// 对应数据库 user 用户表
public class User {
// 所有属性和数据库字段完全对应
private Integer id;
private String name;
private Integer age;
private String phone;
// 必须提供Get、Set方法,用于属性赋值和取值
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
}2. Mapper 数据层(仓库保管员)
核心作用:只负责操作数据库,执行增删改查SQL语句。不对接前端、不做业务判断,纯数据操作层。
Mapper 固定分为两部分:Mapper接口 + MapperXML文件,搭配Mybatis使用。
专属规则
✅ 只做:编写、执行SQL语句,存取数据库数据
❌ 不做:参数校验、业务逻辑、权限判断
实战代码
第一步:Mapper接口,定义数据库操作方法
import com.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
// 标识为数据库操作接口,交给Spring管理
@Mapper
public interface UserMapper {
// 定义根据ID查询用户的方法
User selectUserById(Integer uid);
}第二步:XML文件,编写具体SQL语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 绑定对应的Mapper接口,路径必须完全一致 -->
<mapper namespace="com.demo.mapper.UserMapper">
<!-- id对应接口方法名,resultType对应返回数据的实体类 -->
<select id="selectUserById" resultType="com.demo.entity.User">
SELECT id,name,age,phone FROM user WHERE id = #{uid}
</select>
</mapper>3. Service 业务层(店长,核心核心!)
核心作用:整个项目的逻辑核心,所有业务规则、参数校验、异常处理、事务控制都写在这里。
简单说:数据库能不能查、参数合不合法、数据有没有问题,全部由Service说了算。
固定结构:Service接口(定义功能) + ServiceImpl实现类(写具体逻辑)
专属规则
✅ 只做:参数校验、业务判断、异常抛出、事务控制、复杂数据处理
❌ 不做:接收前端请求、定义接口地址、直接操作数据库
实战代码
第一步:Service接口,声明业务功能
import com.demo.entity.User;
public interface UserService {
// 定义根据ID获取用户的业务功能
User getUserById(Integer id);
}第二步:ServiceImpl实现类,编写具体业务逻辑
import com.demo.entity.User;
import com.demo.mapper.UserMapper;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// 交给Spring容器管理
@Service
public class UserServiceImpl implements UserService {
// 注入Mapper,调用数据库操作方法
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Integer id) {
// 1. 第一层校验:校验前端传入的参数是否合法
if (id == null || id < 1) {
throw new RuntimeException("用户ID参数不合法!");
}
// 2. 调用Mapper查询数据库数据
User user = userMapper.selectUserById(id);
// 3. 第二层校验:判断数据是否存在
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
// 校验全部通过,返回数据
return user;
}
}4. Controller 控制层(对外收银窗口)
核心作用:项目唯一的对外入口,只负责对接前端。接收前端参数、调用Service业务、返回JSON数据,不写任何业务逻辑。
专属规则
✅ 只做:接收前端请求、传递参数、调用业务层、返回JSON结果
❌ 不做:参数判断、业务逻辑、操作数据库
实战代码
import com.demo.entity.User;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
// 接口返回JSON格式数据
@RestController
// 解决前端跨域问题
@CrossOrigin
// 接口统一前缀
@RequestMapping("/user")
public class UserController {
// 注入业务层
@Autowired
private UserService userService;
// 对外开放查询接口:GET请求,地址 /user/find
@GetMapping("/find")
public User findUser(@RequestParam Integer id) {
// 极简逻辑:只调用业务层,直接返回结果
return userService.getUserById(id);
}
}二、完整接口执行流程(一步不落看懂全过程)
我们以前端请求 http://localhost:8080/user/find?id=1 为例,完整流程自上而下、原路返回:
前端发起请求:携带参数 id=1 访问后端接口;
Controller接收请求:拿到参数,直接交给Service业务层;
Service处理业务:校验参数合法性,校验通过后调用Mapper;
Mapper操作数据库:执行编写好的SQL语句,查询MySQL数据;
Entity封装数据:数据库查询出的结果,自动封装到User实体类中;
数据逐层返回:Entity → Mapper → Service → Controller,最终Controller将数据转为JSON格式,返回给前端。
三、接口自测 + 前端对接教程
接口写完不要直接对接前端,先自测,确保后端无问题,再联调前端。
1. 后端自测(Postman)
请求方式:GET
请求地址:
http://localhost:8080/user/find请求参数:Params 中添加 id=1
发送请求后,正常返回用户JSON数据,说明接口开发完成。
2. 前端对接三种常用写法
所有前端对接原理一致,仅语法不同,主流三种场景全覆盖:
① Vue 项目(企业最常用)
import axios from 'axios'
// 根据ID查询用户
async function getUser() {
let result = await axios.get('http://localhost:8080/user/find', {
params: { id: 1 }
})
console.log(result.data)
}② 原生 HTML/JS
fetch("http://localhost:8080/user/find?id=1")
.then(res => res.json())
.then(data => {
console.log("后端数据:", data)
})③ 微信小程序
wx.request({
url: "http://localhost:8080/user/find?id=1",
method: "GET",
success: function (res) {
console.log(res.data)
}
})四、新手高频坑:前端跨域报错
报错现象:前端调用接口,浏览器控制台提示「跨域访问被阻止」。
报错原因:后端运行端口8080,前端网页端口不一致,浏览器的安全机制会拦截跨域请求。
终极解决办法:直接在Controller类上添加 @CrossOrigin 注解,一步解决跨域问题,也是项目通用方案。
五、GET/POST 接口核心区别(必记)
日常开发90%的接口,都离不开这两种请求,区分清楚不踩坑:
@GetMapping 查询请求:用于查询数据,参数拼接在URL地址后,后端用
@RequestParam接收参数;@PostMapping 新增/修改请求:用于提交、修改数据,参数放在请求体中,后端用
@RequestBody接收实体参数。
六、后端写接口固定流程(直接套模板)
最后给大家总结一套通用开发流程,以后写任何接口都按这个顺序来,规范不出错:
根据数据库表,创建对应的Entity实体类;
编写Mapper接口和XML文件,完成数据库SQL操作;
编写Service接口和实现类,完善业务校验、异常逻辑;
编写Controller接口,对外开放请求地址;
Postman自测接口,保证数据正常返回;
对接前端,完成页面数据渲染。
结语
后端四层架构的核心本质就是分层解耦:各司其职、互不干扰。数据封装、数据库操作、业务逻辑、对外接口完全拆分,代码整洁、便于维护,出了问题也能快速定位对应层级。
新手不用死记硬背,跟着这个流程写两遍,就能彻底吃透后端基础接口开发!