mybatis通过jdk动态代理为mapper接口生成代理对象,调用sqlsession.getmapper时创建mapperproxy实例,所有方法调用由invoke拦截并委托sqlsession执行,全程不生成class文件、仅运行时内存代理。

MyBatis 通过 JDK 动态代理为 Mapper 接口生成实现类,核心在于 SqlSession 获取 Mapper 时触发代理对象创建,而非编译期生成具体 class 文件。
Mapper 接口本身不提供实现
你写的 Mapper 接口(如 UserMapper)只有方法声明,没有实现类。MyBatis 不要求你写实现类,也不用注解或 XML 去“标注实现逻辑”,而是靠运行时动态代理接管所有方法调用。
- 接口必须被 MyBatis 扫描到(通过
@Mapper、@MapperScan或 XML 配置) - 接口方法需与 SQL 映射匹配(方法名+参数类型对应 XML 中的
<select></select>ID,或符合注解规则) - MyBatis 会为每个 Mapper 接口创建一个
MapperProxy实例,作为 JDK 动态代理的InvocationHandler
SqlSession#getMapper 触发代理生成
调用 sqlSession.getMapper(UserMapper.class) 时,MyBatis 并不返回真实实现类实例,而是:
- 检查该接口是否已注册为 Mapper(即是否在
Configuration#mapperRegistry中) - 使用
MapperProxyFactory<t></t>创建代理工厂 - 调用
newMapperProxy方法,传入SqlSession和接口类型,最终通过Proxy.newProxyInstance生成代理对象
生成的代理对象类型仍是 UserMapper,但底层是 MapperProxy,所有方法调用都会落入其 invoke 方法中处理。
MapperProxy#invoke 处理实际逻辑
每次调用 Mapper 方法(如 userMapper.selectById(1)),代理会执行 MapperProxy.invoke:
- 根据方法签名查找对应的
MappedStatement(封装了 SQL、参数映射、结果映射等) - 调用
SqlSession.selectOne / selectList / update等方法,把请求委托给真正的执行器(Executor) - 自动完成参数绑定、SQL 渲染、结果集映射、事务管理等,对用户完全透明
也就是说,你看到的是接口调用,背后走的是完整的 MyBatis 执行链路 —— 从代理拦截 → 映射定位 → SQL 执行 → 结果封装。
不是 CGLIB,也不是字节码增强
MyBatis 默认使用 JDK 动态代理(要求 Mapper 必须是接口),不依赖第三方字节码库:
- 不生成 .class 文件,不修改类结构,纯运行时内存中构造代理对象
- 无法代理类(只能代理接口),所以 Mapper 必须是 interface
- 若需要代理类(比如某些特殊场景),需自行集成 CGLIB 或 ByteBuddy,但 MyBatis 官方不支持也不推荐
整个过程轻量、标准、可预测,也是 MyBatis 简洁设计的关键之一。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











