通俗拆解Spring IoC/DI+三层调用全流程|苍穹外卖DAY2
前言学习苍穹外卖第 2 天。今天不满足于 代码能跑就行我决定彻底搞清楚 Spring 最核心的两个概念IoC控制反转和DI依赖注入。然后用一个真实的接口把 Controller → Service → Mapper 的完整调用链走一遍。一、传统方式我自己招人干活想象一下没有 Spring 框架我要开一家餐厅。我是老板Service需要一个切菜工Mapper来干活。我只能自己去招人public class EmployeeServiceImpl { // 我自己负责找和创建这个切菜工 private EmployeeMapper employeeMapper new EmployeeMapperImpl(); public void login() { employeeMapper.getByUsername(...); // 然后才能用他 } }这么做的问题强依赖必须精确知道去哪找切菜工EmployeeMapperImpl实现类变更代码必须修改职责不清既要写业务逻辑又要负责对象创建关注点混乱浪费资源多个 Service 依赖同一个 Mapper需重复创建对象无法复用二、Spring 方式中介公司包办一切Spring 框架开了一家强大的 中介公司学名叫做IoC 容器控制反转容器。核心概念控制反转IoC把 创建对象、管理对象 的控制权从开发者代码交给 Spring 容器依赖注入DI组件需要的依赖对象由容器自动装配进来无需手动 new所有 BeanService/Mapper/Controller 等在程序启动时被容器创建、管理需要依赖时容器自动注入实现别找我我会找你的设计哲学。三、代码里的体现Service // 1. 注册为Bean交给Spring管理 public class EmployeeServiceImpl { Autowired // 2. 声明依赖容器自动注入 private EmployeeMapper employeeMapper; // 3. 无需手动创建直接使用 public void login() { employeeMapper.getByUsername(...); // 4. 直接调用 } }流程拆解扫描注册Spring 启动扫描 Service/Mapper创建实例存入 IoC 容器依赖注入创建 EmployeeServiceImpl 时发现 Autowired从容器中取出 EmployeeMapper 注入使用对象属性直接可用全局单例节约资源便于管理四、为什么要用 IoC/DI核心价值解耦只依赖接口不依赖具体实现类替换实现类时业务代码无需修改统一管理对象生命周期支持单例 / 多例等策略一句话总结控制反转IoC交出对象创建与管理的控制权依赖注入DI需要的依赖框架自动送过来五、实战启用 / 禁用员工完整调用链以启用 / 禁用员工功能为例打通三层调用全流程。需求管理员点击禁用按钮将员工账号状态改为停用0。前端请求POST /admin/employee/status/0?id3含义ID3 的员工状态设为 0禁用第一棒Controller 层接待员负责接收请求、参数解析、调用 Service、返回统一结果// EmployeeController.java PostMapping(/status/{status}) ApiOperation(启用禁用员工账号) public Result startOrStop(PathVariable Integer status, Long id) { log.info(启用禁用员工账号{},{}, status, id); employeeService.startOrStop(status, id); return Result.success(); }解析 URL 路径参数status与请求参数id不处理业务直接转发给 Service封装 Result 返回前端第二棒Service 层业务处理层负责业务逻辑编排、数据组装// EmployeeServiceImpl.java Override public void startOrStop(Integer status, Long id) { // 组装要更新的数据 Employee employee Employee.builder() .status(status) .id(id) .updateTime(LocalDateTime.now()) .updateUser(BaseContext.getCurrentId()) .build(); // 交给Mapper执行 employeeMapper.update(employee); }踩坑记录// 错误写法build()返回新对象未接收传入空对象 Employee employee new Employee(); Employee.builder().status(status).id(id).build(); employeeMapper.update(employee); // SQL条件idnull✅ 正确写法用变量接收builder().build()返回的对象第三棒Mapper 层数据访问层Mapper 接口// EmployeeMapper.java Mapper public interface EmployeeMapper { void update(Employee employee); }XML SQL 映射EmployeeMapper.xml -- update idupdate parameterTypeEmployee update employee name ! nullname #{name}, username ! nullusername #{username}, password ! nullpassword #{password}, phone ! nullphone #{phone}, sex ! nullsex #{sex}, idNumber ! nullid_number #{idNumber}, ! nullupdate_time #{updateTime}, updateUser ! nullupdate_user #{updateUser}, nullstatus #{status}, id #{id} /updateMyBatis 执行流程根据接口 方法名匹配 SQL 模板解析动态拼接 SQL替换#{}占位符生成可执行 SQL执行并返回结果最终执行 SQLupdate employee set status 0, update_time 2026-08-01..., update_user 1 where id 3结果返回链路数据库更新成功 → Mapper 返回 void → Service 返回 void → Controller 封装 Result.success() → 前端收到 {code: 1, msg: 操作成功}六、完整调用链路图前端 HTTP 请求 ↓ Controller 层接收请求、参数解析 | 数据形态零散参数 → 传给Service ↓ Service 层业务编排、对象组装 | 数据形态零散参数 → Employee实体 ↓ Mapper 层SQL生成、数据操作 | 数据形态实体 → 执行SQL ↓ 数据库执行 ↓ 结果层层返回 → Controller封装 → 前端响应结尾今天终于搞懂了IoC/DI看课的时候一直云里雾里感觉跟着敲完啥都没学到打通了Controller→Service→Mapper三层调用全流程终于是觉得学到东西了。如果你也在学 Spring希望这篇博客能帮你把 IoC、DI 和三层调用链彻底吃透 欢迎评论区交流一起进步