MyBatis Mapper代理对象中SqlSession需通过反射获取:先用Proxy.getInvocationHandler()获取MapperProxy,再用getDeclaredField("sqlSession")配合setAccessible(true)读取。该方式依赖内部实现,存在版本兼容风险,仅建议用于调试或特殊扩展。

MyBatis 的 Mapper 接口本身是接口,运行时由 MyBatis 通过 JDK 动态代理生成代理对象,该代理对象内部持有 SqlSession 实例(实际是 MapperProxy 持有 SqlSession),但这个引用并未对外暴露——不是 public 字段,也不提供 getter 方法。因此,若需通过反射获取,核心在于定位代理对象背后的 MapperProxy 实例及其 sqlSession 字段。
确认代理对象的真实类型
MyBatis 的 Mapper 代理对象是 java.lang.reflect.Proxy 实例,其 InvocationHandler 是 org.apache.ibatis.binding.MapperProxy(MyBatis 3.4+)。不能直接对代理对象调用 getClass().getDeclaredFields(),因为代理类本身不含 sqlSession 字段——它藏在 handler 里。
- 先用
Proxy.getInvocationHandler(mapper)获取 handler - 再判断 handler 是否为
MapperProxy类型(避免强转失败) - 若类型匹配,继续反射访问其私有字段
反射读取 MapperProxy 中的 sqlSession 字段
MapperProxy 类中,sqlSession 是一个 final、private 的字段(类型为 SqlSession),位于类定义顶层(非继承自父类)。反射时需注意:
- 使用
getField("sqlSession")会失败(因 private),必须用getDeclaredField("sqlSession") - 调用
setAccessible(true)绕过访问控制 - 从
MapperProxy实例上调用get()获取值
示例代码片段:
立即学习“Java免费学习笔记(深入)”;
Object handler = Proxy.getInvocationHandler(mapper);
if (handler instanceof MapperProxy) {
Field sqlSessionField = MapperProxy.class.getDeclaredField("sqlSession");
sqlSessionField.setAccessible(true);
SqlSession sqlSession = (SqlSession) sqlSessionField.get(handler);
// ✅ 获取成功
}
兼容性与注意事项
该方式依赖 MyBatis 内部实现细节,不同版本字段名或类结构可能变化(如旧版 MyBatis 3.2 使用 MapperProxyFactory 等间接路径):
- MyBatis 3.4.0+:字段名确为
"sqlSession",类型为SqlSession - Spring 整合场景下,若 Mapper 被 Spring AOP 二次代理(如加了事务切面),需递归解包:先剥掉 Spring 的
Advised代理,再取底层 MyBatis 代理 - 生产环境慎用:违反封装,易随框架升级失效;仅建议用于调试、监控或特殊扩展(如自定义 SQL 执行上下文)
替代方案:更安全的获取方式
如果目标是获取 SqlSession 用于执行额外操作,优先考虑以下正向途径:
- 通过
SqlSessionFactory.openSession()显式获取新会话(注意手动管理生命周期) - 在
MapperProxy所在线程中,从SqlSessionManager或TransactionSynchronizationManager(Spring)中提取当前绑定的会话 - 自定义
MapperFactoryBean或MapperScannerConfigurer扩展,注入可访问的SqlSession引用
反射虽可行,但应作为最后手段。理解代理链路和设计意图,比强行“撬开”更可持续。


















