1. MyBatis Mapper 接口的魔法:为什么能直接@Autowired?
第一次在Spring项目里看到@Autowired注入MyBatis Mapper接口时,我盯着屏幕愣了三秒——这玩意儿明明是个接口啊!按照Java基础课教的,接口不能实例化,那Spring是怎么把Mapper接口变成Bean的?后来扒了MyBatis-Spring的源码才知道,这里面的动态代理玩得真是溜。今天咱们就掰开揉碎看看,MyBatis和Spring这对好基友是怎么联手实现这个"违背祖宗决定"的特性的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心机制解析
2.1 MyBatis-Spring的桥梁作用
MyBatis自己单独用的时候,需要通过SqlSession的getMapper()方法获取Mapper实例。但到了Spring体系里,这个手动获取的过程就被自动化了。关键就在于mybatis-spring这个整合包,它主要干了三件大事:
- MapperScannerConfigurer:这个后置处理器会扫描指定包路径下的Mapper接口
- MapperFactoryBean:为每个Mapper接口创建对应的FactoryBean
- 动态代理生成:最终生成的其实是接口的代理实现类
重要提示:如果你在Spring Boot项目里用了@MapperScan注解,本质上就是在配置MapperScannerConfigurer
2.2 动态代理的两种实现方式
MyBatis对Mapper接口的代理有两种实现路径:
-
JDK动态代理(默认方式)
- 要求Mapper必须是接口
- 运行时生成$Proxy开头的代理类
- 代理类会拦截所有方法调用,转交给MapperProxy处理
-
CGLIB代理(需要特殊配置)
- 可以代理具体类
- 生成形如Mapper$$EnhancerByCGLIB的代理类
- 性能略低于JDK动态代理
java复制// 典型配置示例(application.properties)
mybatis.mapper-proxy=JDK // 或CGLIB
2.3 生命周期全流程
-
启动阶段:
- Spring容器初始化时,MapperScannerConfigurer开始扫描
- 为每个Mapper接口注册MapperFactoryBean
- FactoryBean的getObject()返回代理实例
-
依赖注入时:
- @Autowired触发Bean获取请求
- 容器返回之前生成的代理对象
- 代理对象的方法调用会被转发到MyBatis的SQL执行引擎
-
方法调用时:
- 代理对象拦截方法调用
- 根据方法名和参数解析对应的SQL语句
- 通过SqlSession执行数据库操作
- 返回处理结果
3. 深度实现剖析
3.1 MapperFactoryBean的魔法
这个FactoryBean是整件事情的核心枢纽,它的继承体系很有意思:
code复制MapperFactoryBean
|- SqlSessionDaoSupport
|- DaoSupport
|- FactoryBean
关键方法getObject()的实现堪称经典:
java复制public T getObject() throws Exception {
return g
