MyBatis Mapper 没有实现类却能注入,因为扫描器为接口注册 FactoryBean,FactoryBean 再创建 Mapper 动态代理。代理执行时从 SqlSessionTemplate 获取与事务同步的 Session。
Table of contents
Open Table of contents
先理解接口代理
MapperProxySample下载用 JDK Proxy 为 UserMapper 创建实现:
mvn -Dtest=SpringSourceLabTest#mapperInterfaceIsBackedByAProxy test
教学代理按方法名返回结果;真实 MyBatis 把方法映射到 MappedStatement,转换参数,执行 Executor,再映射结果。
Mapper 如何进入 Spring
@MapperScan 导入 Registrar,扫描接口并注册 MapperFactoryBean 定义。容器获取 Mapper 类型时,FactoryBean 的 getObject() 调用 MyBatis Configuration 创建 MapperProxy。
这条链解释了为什么:依赖树里有 Mapper 接口不代表它已经是 Bean;扫描包、SqlSessionFactory 和 FactoryBean 任一环缺失都会注入失败。
SqlSessionTemplate 解决线程与事务问题
原生 SqlSession 不是线程安全对象。SqlSessionTemplate 作为线程安全代理,每次调用取得当前事务绑定的 Session;无事务时创建并在调用后关闭。
Spring 事务管理器把 JDBC Connection 绑定到当前线程,MyBatis-Spring 通过事务同步复用同一连接。不要手工在同一业务事务里新建独立 SqlSession,否则可能绕开提交与回滚。
常见错误
- Mapper 扫描包过宽,测试或旧接口意外注册;
- 同一 Mapper 同时使用注解和 XML,statement ID 冲突;
- 在循环中调用 Mapper 造成 N+1;
- 批处理没有控制批次和内存;
- 异步线程误以为继承了原事务。
本篇只解释整合边界;下一阶段会沿 MyBatis Configuration、Executor 和 StatementHandler 深入 SQL 执行。
下一步
阅读手写 Spring Boot 核心流程,把主配置、Context 和嵌入式服务器组合成启动器。