很多刚学后端的朋友,写代码只会照搬模板,完全搞不懂 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接口,路径必须完全一致 -->
&lt;mapper namespace="com.demo.mapper.UserMapper"&gt;
    <!-- 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 为例,完整流程自上而下、原路返回:

  1. 前端发起请求:携带参数 id=1 访问后端接口;

  2. Controller接收请求:拿到参数,直接交给Service业务层;

  3. Service处理业务:校验参数合法性,校验通过后调用Mapper;

  4. Mapper操作数据库:执行编写好的SQL语句,查询MySQL数据;

  5. Entity封装数据:数据库查询出的结果,自动封装到User实体类中;

  6. 数据逐层返回: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 接收实体参数。

六、后端写接口固定流程(直接套模板)

最后给大家总结一套通用开发流程,以后写任何接口都按这个顺序来,规范不出错:

  1. 根据数据库表,创建对应的Entity实体类;

  2. 编写Mapper接口和XML文件,完成数据库SQL操作;

  3. 编写Service接口和实现类,完善业务校验、异常逻辑;

  4. 编写Controller接口,对外开放请求地址;

  5. Postman自测接口,保证数据正常返回;

  6. 对接前端,完成页面数据渲染。

结语

后端四层架构的核心本质就是分层解耦:各司其职、互不干扰。数据封装、数据库操作、业务逻辑、对外接口完全拆分,代码整洁、便于维护,出了问题也能快速定位对应层级。

新手不用死记硬背,跟着这个流程写两遍,就能彻底吃透后端基础接口开发!