一、写在前面很多刚接触 Spring Boot 的同学都会有这些疑问为什么有的地方用Autowired为什么现在又推荐“构造器注入”Mapper到底是干嘛的为什么没有实现类也能用Controller / Service / Mapper 的注入方式为什么看起来不一样这篇文章我不讲表面用法而是从本质原理讲清楚Spring 到底是如何管理对象的MyBatis 是如何“生成 Mapper”的为什么构造器注入是更优解二、核心一句话先记住Spring 的核心不是注解而是容器 Bean 依赖关系三、Spring 容器到底在干嘛你可以把 Spring 容器理解成一个“对象仓库”。Spring 容器 - 存放各种对象Bean - 管理对象生命周期 - 负责依赖注入四、Bean 是怎么来的1️⃣ Controller / ServiceRestController Service Spring 扫描这些注解 → 自动创建对象 → 放入容器2️⃣ Mapper重点Mapper public interface UserMapper { } 注意这是接口没有实现类那它怎么变成 Bean 的MyBatis 的作用MyBatis 会为 Mapper 接口生成一个“代理对象” → 再把这个代理对象注册到 Spring 容器 所以本质是UserMapper ≠ 普通类 UserMapper MyBatis 生成的代理对象五、三层结构关系必须搞懂Controller → Service → Mapper → 数据库举个例子RestController public class UserController { private final UserService userService; } Service public class UserServiceImpl { private final UserMapper userMapper; } 本质Controller 依赖 Service Service 依赖 Mapper Mapper 操作数据库六、依赖注入的两种方式❌ 方式1字段注入AutowiredAutowired private UserService userService; 执行流程1. 先 new 对象 2. 再通过反射注入依赖 问题对象创建时不完整 ❌依赖不明显 ❌不利于测试 ❌✅ 方式2构造器注入推荐private final UserService userService; public UserController(UserService userService) { this.userService userService; } 执行流程new UserController(userService) 优点对象一创建就是完整的 ✔依赖清晰 ✔支持 final ✔更安全 ✔七、为什么推荐构造器注入1️⃣ 保证对象完整性字段注入如果你在构造方法里打印依赖public UserServiceImpl() { System.out.println(userMapper); //可能还是 null ❌ }因为这时候对象刚创建依赖还没注入。构造器注入public UserServiceImpl(UserMapper userMapper) { System.out.println(userMapper); // 有值 ✔ }说明对象从出生开始就是完整的。2️⃣ 依赖更清晰public UserServiceImpl(UserMapper userMapper) 一眼就知道依赖谁3️⃣ 支持 final更安全private final UserMapper userMapper; 依赖不会被修改 依赖初始化后就不能随便被改掉代码更稳。八、Mapper 到底干了什么Mapper public interface UserMapper { } 它的作用是告诉 MyBatis 这是一个 Mapper 接口 → 为它生成代理对象 → 注册到 Spring 容器补充企业级用法MapperScan重点在实际企业项目中通常不会在每个 Mapper 上写Mapper而是使用统一扫描。1️⃣ 企业推荐写法 ⭐SpringBootApplication MapperScan(com.xxx.project.infrastructure.persistence.mapper) public class ProjectApplication { }然后public interface UserMapper { } 注意Mapper 接口上 不用写 Mapper2️⃣ 本质区别非常关键Mapper作用在“单个接口”→ 标记这个接口需要生成代理MapperScan作用在“包”→ 扫描整个包→ 批量生成 Mapper 代理对象→ 注册到 Spring 容器3️⃣ 为什么企业用 MapperScan项目一大可能有- UserMapper- OrderMapper- PayMapper- InventoryMapper...几十甚至上百个 Mapper 如果每个都写Mapper问题冗余 ❌容易漏 ❌不统一 ❌4️⃣ 企业标准结论必须记住小项目✔ Mapper企业项目✔ MapperScan主流标准5️⃣ 再升一级面试加分点MapperScan 本质通过 ImportBeanDefinitionRegistrar动态注册 Mapper Bean个人推荐MapperScan Mapper 双保险更严谨MapperScan( basePackages org.example.arkbackend.mapper, annotationClass Mapper.class )同时接口上Mapper public interface UserMapper { }这时候它的含义是1、扫描指定包2、但不是见接口就处理 而是只处理带 Mapper 的接口这就是“双保险”。好处在于包范围有约束注解标记也有约束更不容易误扫在规范要求高的项目里更严谨方案对比方案是否推荐说明全部写Mapper✅简单直观适合学习和小项目只用MapperScan✅企业常用简洁高效MapperScanMapper更严谨避免误扫九、一个完整流程重点理解1. Spring 启动2. 扫描 Service / Controller → 创建 Bean3. MyBatis 扫描 Mapper → 生成代理对象4. 所有对象放入 Spring 容器5. 构造器注入依赖6. Controller 调用 Service7. Service 调用 Mapper8. Mapper 执行 SQL十、最终推荐写法企业标准ControllerRestController RequiredArgsConstructor RequestMapping(/user) public class UserController { private final UserService userService; }ServiceService RequiredArgsConstructor Slf4j public class UserServiceImpl implements UserService { private final UserMapper userMapper; }MapperMapper public interface UserMapper { } 统一规则所有依赖注入一律使用 构造器注入 final Lombok十一、常见误区总结❌ 误区1Mapper 不需要注入 错Mapper 也是 Bean一样通过依赖注入使用❌ 误区2Autowired 是必须的 错构造器注入不需要 AutowiredSpring 自动识别❌ 误区3Controller 可以随便写 错Controller / Service / Mapper 应该统一规范十二、总结一句话Controller、Service、Mapper 本质一样都是 Spring 容器里的 Bean区别只是“谁创建它”。十三、最后如果你刚学到这里建议你立刻做一件事 写一个完整链路Controller → Service → Mapper → 查询数据库 → 返回结果这是从“理解框架”到“能干活”的关键一步。