MVC-三层架构详解

一、整体架构图(三层架构)

bash 复制代码
┌─────────────────────────────────────────────────────┐
│                    前端 (Vue/React)                  │  用户界面
│         http://localhost:5173/employee/list         │
└──────────────────┬──────────────────────────────────┘
                   │ HTTP请求
                   ↓
┌─────────────────────────────────────────────────────┐
│              Controller 层(控制器)                  │  接收请求
│   @RestController                                   │
│   EmployeeInfoController.java                       │  ← 在 hrone-admin 模块
│   - 接收HTTP请求                                     │
│   - 参数验证                                         │
│   - 调用Service                                      │
└──────────────────┬──────────────────────────────────┘
                   │ 调用业务方法
                   ↓
┌─────────────────────────────────────────────────────┐
│              Service 层(业务逻辑)                   │  处理业务
│   @Service                                          │
│   EmployeeInfoServiceImpl.java                      │  ← 在 hrone-employeeManagement 模块
│   - 业务逻辑处理                                     │
│   - 默认值设置 ⭐                                    │
│   - 数据转换                                         │
│   - 调用Mapper                                       │
└──────────────────┬──────────────────────────────────┘
                   │ 调用数据访问方法
                   ↓
┌─────────────────────────────────────────────────────┐
│              Mapper 层(数据访问)                    │  访问数据库
│   @Mapper                                           │
│   EmployeeInfoMapper.java (接口)                    │  ← 在 hrone-employeeManagement 模块
│   EmployeeInfoMapper.xml (SQL实现)                  │
│   - 定义SQL语句                                      │
│   - 执行数据库操作                                   │
└──────────────────┬──────────────────────────────────┘
                   │ SQL查询
                   ↓
┌─────────────────────────────────────────────────────┐
│                 数据库 (MySQL)                       │  存储数据
│              tb_employee_info 表                     │
└─────────────────────────────────────────────────────┘

二、一次完整请求的生命周期

让我用查询员 工列 的实际例子来说明:

第1步:用户操作

bash 复制代码
// 用户在浏览器打开员工管理页面
    // 前端发送请求
    GET http://localhost:8085/base/employeeInfo/list?pageNum=1&pageSize=10

第2步:Controller 接收请求

java 复制代码
//EmployeeInfoController.java

@RestController
@RequestMapping("/employeeInfo")
public class EmployeeInfoController extends BaseController {

    @Autowired
    private IEmployeeInfoService EmployeeInfoService;  // ← 注入Service

    @GetMapping("/list")  // ← 这个方法接收 GET /employeeInfo/list 请求
    public TableDataInfo list(@RequestParam MultiValueMap<String, String> allRequestParams) {
        // 1. 接收到参数:pageNum=1, pageSize=10
        System.out.println("收到请求参数: " + allRequestParams);

        // 2. 调用Service层处理业务
        List<Map<String, ?>> list = EmployeeInfoService.selectEmployeeInfoByAttributes(allRequestParams);
        //                          ↑ 调用Service层的方法

        // 3. 封装结果返回给前端
        return getDataTable(list);
    }
}

作用

  • 🎯 负责接收HTTP请求
  • 📦 提取请求参数
  • 🔗 调用Service层的业务方法
  • 📤 返回JSON响应给前端

第3步:Service 处理业务逻辑

java 复制代码
//EmployeeInfoServiceImpl.java

@Service
public class EmployeeInfoServiceImpl implements IEmployeeInfoService {
    
    @Autowired
    private EmployeeInfoMapper employeeInfoMapper;  // ← 注入Mapper
    
    @Override
    public List<Map<String, ?>> selectEmployeeInfoByAttributes(MultiValueMap<String, String> allRequestParams) {
        
        // 1️⃣ 业务逻辑:设置默认值(您刚修改的部分)
        LinkedMultiValueMap<String, Object> equalsMap = new LinkedMultiValueMap<>();
        
        // 如果用户没有指定 employee_active,默认只查询在职员工
        if (!allRequestParams.containsKey("employee_active")) {
            equalsMap.add("employee_active", true);  // ← 默认值逻辑
        }
        
        // 2️⃣ 业务逻辑:参数解析、类型转换等
        // ... 省略中间代码 ...
        
        // 3️⃣ 调用Mapper层执行SQL查询
        return employeeInfoMapper.searchEmployeeAttributes(systemColumnIds, clientAttributeTypes, likeMap, equalsMap);
        //     ↑ 调用Mapper接口的方法
    }
}

作用

  • 🧠 处理业务逻辑(如设置默认值)
  • 🔄 数据转换和验证
  • 🗄️ 调用Mapper执行数据库操作

第4步:Mapper 执行SQL

4.1 Mapper 接口(定义方法)
java 复制代码
//EmployeeInfoMapper.java

@Mapper
public interface EmployeeInfoMapper {
    
    // 定义方法签名,但不写实现
    List<Map<String, Object>> searchEmployeeAttributes(
        @Param("systemColIds") List<String> systemColIds,
        @Param("clientAttributeTypes") Map<String, String> clientAttributeTypes,
        @Param("likeMap") MultiValueMap<String, String> likeMap,
        @Param("equalsMap") MultiValueMap<String, Object> equalsMap
    );
    // ↑ 这只是接口定义,真正的SQL在XML文件中
}
4.2 Mapper XML(SQL实现)
XML 复制代码
//EmployeeInfoMapper.xml

<mapper namespace="com.hrone.base.employeeManagement.mapper.EmployeeInfoMapper">
    <!-- 这个id对应Java接口中的方法名 -->
    <select id="searchEmployeeAttributes" resultType="hashmap">
        SELECT * FROM (
            SELECT employee_id, employee_name, employee_active, ...
            FROM tb_employee_info
        ) AS parsed
        <where>
            <!-- 处理equalsMap中的条件 -->
            <if test="equalsMap != null and equalsMap.size() > 0">
                <foreach collection="equalsMap" item="valuelist" index="key">
                    `${key}` IN
                    <foreach collection="valuelist" item="value" open="(" close=")" separator=",">
                        #{value}
                    </foreach>
                </foreach>
            </if>
        </where>
    </select>
</mapper>

这一步生成的SQL:

sql 复制代码
SELECT * FROM (
    SELECT employee_id, employee_name, employee_active, ...
    FROM tb_employee_info
) AS parsed
WHERE employee_active IN (1)  -- ← 这个1就是Service层设置的true

作用

  • 📝 定义具体的SQL语句
  • 🔀 动态拼接WHERE条件
  • 🗃️ 执行数据库查询

第5步:数据库返回结果

bash 复制代码
MySQL数据库执行SQL → 返回数据 → Mapper → Service → Controller → 前端

三、模块之间的关系

bash 复制代码
┌─────────────────────────────────────────────┐
│          hrone-base-server (父项目)          │
│                  pom.xml                    │  ← 管理所有子模块
└─────────────────────┬───────────────────────┘
                      │
          ┌───────────┴───────────┬─────────────┐
          │                       │             │
┌─────────▼─────────┐  ┌─────────▼─────────┐  ┌▼─────────────┐
│   hrone-admin     │  │ hrone-employee    │  │ hrone-common │
│   (主应用模块)     │  │   Management      │  │  (公共工具)   │
│                   │  │  (员工管理模块)    │  │              │
│ ✅ Controller     │  │                   │  │ ✅ 工具类    │
│ ✅ 启动类         │  │ ✅ Service        │  │ ✅ 基础类    │
│ ✅ 配置文件       │  │ ✅ Mapper         │  │ ✅ 注解      │
│                   │  │ ✅ Domain         │  │              │
└─────────┬─────────┘  └─────────┬─────────┘  └──────────────┘
          │                       │
          │  依赖关系 (pom.xml)   │
          │   <dependency>        │
          └──────────►────────────┘

依赖关系说明:

java 复制代码
<!-- hrone-admin 的 pom.xml -->
<dependencies>
    <!-- 依赖员工管理模块 -->
    <dependency>
        <groupId>com.hrone</groupId>
        <artifactId>hrone-employeeManagement</artifactId>
    </dependency>
    
    <!-- 依赖公共模块 -->
    <dependency>
        <groupId>com.hrone</groupId>
        <artifactId>hrone-common</artifactId>
    </dependency>
</dependencies>

意思是

  • hrone-admin 可以使用 hrone-employeeManagement 中的所有类
  • Controller 在 hrone-admin,Service/Mapper 在 hrone-employeeManagement
  • 通过 @Autowired 就能互相调用

四、代码调用链路(实际例子)

以您修改的代码为例:

java 复制代码
// 1️⃣ 前端请求
GET /employeeInfo/list

         ↓

// 2️⃣ Controller接收(hrone-admin模块)
@GetMapping("/list")
public TableDataInfo list(@RequestParam MultiValueMap<String, String> allRequestParams) {
    List<Map<String, ?>> list = EmployeeInfoService.selectEmployeeInfoByAttributes(allRequestParams);
    return getDataTable(list);
}

         ↓

// 3️⃣ Service处理(hrone-employeeManagement模块)
public List<Map<String, ?>> selectEmployeeInfoByAttributes(MultiValueMap<String, String> allRequestParams) {
    
    // ⭐ 您修改的代码在这里
    if (!allRequestParams.containsKey("employee_active")) {
        equalsMap.add("employee_active", true);  // 默认只查在职员工
    }
    
    // 调用Mapper
    return employeeInfoMapper.searchEmployeeAttributes(...);
}

         ↓

// 4️⃣ Mapper接口(hrone-employeeManagement模块)
List<Map<String, Object>> searchEmployeeAttributes(...);

         ↓

// 5️⃣ Mapper XML(hrone-employeeManagement/resources/mapper)
<select id="searchEmployeeAttributes">
    SELECT * FROM tb_employee_info
    WHERE employee_active = #{value}  ← 这里的value就是true
</select>

         ↓

// 6️⃣ MySQL数据库
执行SQL: SELECT * FROM tb_employee_info WHERE employee_active = 1

         ↓

// 7️⃣ 返回数据
数据库 → Mapper → Service → Controller → 前端

五、关键概念对比

|-------------------|-------|---|-----------|---------------|
| 层级 | 文件类型 | | 作用 | 类比 |
| Controller | .java | | 接收请求、返回响应 | 餐厅的服务员(接单、上菜) |
| Service | .java | | 业务逻辑处理 | 餐厅的经理(决定怎么做) |
| Mapper接 | .java | | 定义数据操作方法 | 菜单(列出有什么菜) |
| Mapper XML | .xml | | SQL语句实现 | 菜谱(具体怎么做菜) |
| Domain | .java | | 数据实体类 | 菜品(数据的样子) |
| Database | MySQL | | 存储数据 | 食材仓库(存放原材料) |

六、为什么要分这么多层?

1. 职责分离

java 复制代码
Controller  → 只管接收请求和返回响应(不管业务逻辑)
Service     → 只管业务逻辑(不管HTTP请求)
Mapper      → 只管数据库操作(不管业务)

2. 代码复用

复制代码
// Service可以被多个Controller调用
EmployeeInfoController → EmployeeInfoService
AttendanceController   → EmployeeInfoService  // 考勤也需要员工信息

3. 易于测试和维护

复制代码
// 单独测试业务逻辑,不需要启动整个服务器
@Test
public void testService() {
    EmployeeInfoService service = new EmployeeInfoServiceImpl();
    // 测试业务逻辑
}

七、总结:记住这个流程就够了

复制代码
用户点击按钮
    ↓
前端发送HTTP请求
    ↓
Controller 接收请求(入口)
    ↓
Service 处理业务逻辑(您修改的地方 ⭐)
    ↓
Mapper 执行SQL语句
    ↓
Database 返回数据
    ↓
原路返回到前端
相关推荐
invicinble2 小时前
关于认识,和优化idea开发
java·ide·intellij-idea
小刘不想改BUG2 小时前
LeetCode 56.合并区间 Java
java·python·leetcode·贪心算法·贪心
Pluchon2 小时前
硅基计划4.0 算法 BFS最短路问题&多源BFS&拓扑排序
java·算法·哈希算法·近邻算法·广度优先·宽度优先·迭代加深
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于Java的星星儿童救助帮扶系统为例,包含答辩的问题和答案
java·开发语言
清晓粼溪2 小时前
SpringBoot3-02:整合资源
java·开发语言·spring boot
CoderYanger2 小时前
C.滑动窗口-求子数组个数-越短越合法——3134. 找出唯一性数组的中位数
java·开发语言·数据结构·算法·leetcode
谷粒.2 小时前
云原生测试:在分布式系统中的质量保障策略
运维·python·测试工具·云原生·架构·自动化·测试覆盖率
ckhcxy2 小时前
继承和多态(二)
java·开发语言
今天你TLE了吗3 小时前
LeeCode Hot100随机链表的复制 java易懂题解
java·数据结构·链表