MyBatis 使用 JDK 动态代理创建 Mapper 接口的代理对象(如 $Proxy17),由 MapperProxy 作为 InvocationHandler 拦截方法调用并委托给 MapperMethod 执行 SQL,不生成任何实现类或字节码文件。

MyBatis 中的 MapperProxy 并不“生成实现类”,而是通过 JDK 动态代理机制,为 Mapper 接口创建一个运行时的代理对象(MapperProxy 实例),所有接口方法调用都会被拦截并转发给 MapperMethod 执行 SQL。它不生成新的 .class 文件,也不使用字节码生成库(如 CGLIB 或 Javassist)。
MapperProxy 是代理对象,不是生成的类
MapperProxy 本身是一个实现了 InvocationHandler 的普通 Java 类。当调用 SqlSession.getMapper(UserMapper.class) 时,MyBatis 调用:
MapperRegistry.getMapper(Class<T> type, SqlSession sqlSession)- 内部委托给
MapperProxyFactory.newInstance(SqlSession) - 该工厂调用
Proxy.newProxyInstance(..., new MapperProxy(sqlSession, mapperInterface, methodCache))
最终返回的是一个 JDK 动态代理对象——它的运行时类型是 $ProxyN(JVM 自动生成的代理类),而 MapperProxy 是其处理器,负责响应所有方法调用。
关键代理逻辑在 invoke 方法中
MapperProxy.invoke(Object proxy, Method method, Object[] args) 是核心入口:
立即学习“Java免费学习笔记(深入)”;
- 如果 method 是
Object自身方法(如toString()、hashCode()),直接执行 - 否则,从缓存或构建
MapperMethod对象(封装了 SQL 解析结果 + 参数处理逻辑) - 调用
mapperMethod.execute(sqlSession, args),真正触发 SQL 执行(SELECT/INSERT/UPDATE/DELETE)
这个过程完全绕过了“实现类”的概念——没有编译期类,也没有运行时类定义,只有代理对象 + 处理器 + 元数据驱动的行为。
为什么不用 CGLIB 或生成 class 文件?
MyBatis 的设计目标是轻量、可预测、易调试:
- Mapper 接口无实现、无继承约束,JDK 动态代理天然适配
- 所有 SQL 行为由 XML 或注解定义,与代理对象解耦
- 避免字节码操作带来的兼容性风险(如 JDK 版本升级、模块系统限制)
- 代理对象行为完全由
MapperMethod控制,便于统一拦截、日志、缓存等扩展
所以 MyBatis 不需要、也不会生成一个 “UserMapperImpl” 这样的具体实现类。
验证方式:打印代理对象类型
在代码中加一行:
System.out.println(userMapper.getClass()); // 输出类似:class com.sun.proxy.$Proxy17
再打印其父类和接口:
System.out.println(Arrays.toString(userMapper.getClass().getInterfaces())); // 包含 UserMapper.class
这清楚表明:它是一个 JDK 代理实例,只实现了你声明的 Mapper 接口,背后逻辑全由 MapperProxy 驱动。


















