MyBatis通过反射机制完成SQL结果到Java对象的封装:先用Class.newInstance()创建实例,再依列名与属性名映射关系,通过Field.set()赋值,支持驼峰转换、嵌套递归及TypeHandler类型适配。

MyBatis 把 SQL 查询结果变成 Java 对象,核心靠的是反射机制,但不是“裸用”反射——它把反射封装在一套可控、可配、可扩展的流程里。Mapper 接口本身不实现方法,真正干活的是 MyBatis 运行时动态生成的代理对象;而结果映射阶段,反射主要用在两处:创建目标对象实例、给字段赋值。
反射在结果封装中的具体作用
执行完 SQL 得到 ResultSet 后,MyBatis 并不手动 new 对象或写 setter,而是通过反射完成:
- 根据 resultType 或 resultMap 指定的类型,调用 Class.getDeclaredConstructor().newInstance() 创建空实例(要求类有无参构造)
- 遍历 ResultSet 的每一列,结合列名(或别名)与 Java 属性名的映射关系,通过 Field.setAccessible(true) + Field.set() 给私有属性直接赋值
- 若字段是基本类型(如 int),会自动处理 null 安全问题(例如用 Integer 包装,再转为 int,默认为 0)
- 对嵌套对象或集合,反射递归触发子对象的创建与赋值,比如 user.getAddress().setCity(...) 这类链式结构也是靠反射层层调用 getter/setter 实现的
Mapper 接口如何“触发”反射映射
你写的 Mapper 接口只是个声明,真正驱动反射的是 MyBatis 的代理机制和执行链:
- 启动时,MyBatis 扫描接口并用 JDK 动态代理(或 CGLIB)生成 MapperProxy 实例,所有方法调用最终落到 MapperMethod.execute()
- execute() 根据 SQL 类型(SELECT/INSERT…)分发,SELECT 走查询流程,进入 Executor → StatementHandler → ResultSetHandler
- DefaultResultSetHandler 是实际执行映射的组件:它读取 ResultSet,按配置解析出要映射的目标类型,然后调用反射工具类(如 Reflector、ObjectWrapper)完成对象构建与赋值
- 整个过程对开发者透明——你只写接口方法签名和 SQL,反射细节由框架内部的 TypeHandler + MetaObject + PropertyTokenizer 协同完成
哪些情况会让反射失败?怎么避免
反射不是万能的,常见报错往往源于“找不到匹配项”或“权限/类型不兼容”,典型场景包括:
- 属性不可访问:Java 字段是 private 但没提供 public setter,且未开启 setAccessible(MyBatis 默认开启,但某些安全策略会禁用)→ 建议保留标准 getter/setter
- 类型不匹配:比如数据库返回 VARCHAR,但字段声明为 LocalDateTime,又没配对应 TypeHandler → 显式注册自定义 TypeHandler 或用 @Results 注解指定 column 和 property 类型
- 字段名完全不对应:user_name 列映射 userName 属性,但没开驼峰转换(mapUnderscoreToCamelCase=false),也没用别名或 resultMap → 开启驼峰、加 AS 别名,或改用 resultMap 显式绑定
- 构造器缺失:目标类只有带参构造,无默认构造 → MyBatis 无法 newInstance → 补一个 public 无参构造,或用 @ConstructorArgs 配置构造注入
反射之外,MyBatis 还做了什么来保障映射稳定
纯靠反射容易脆弱,MyBatis 加了多层保护和优化:
- 缓存反射元信息:首次使用某类时,MyBatis 用 Reflector 缓存其所有可写属性、setter 方法、getter 方法,后续复用,避免重复反射开销
- TypeHandler 统一类型桥接:把 JDBC 类型(TIMESTAMP)→ Java 类型(LocalDateTime)的转换逻辑抽离,支持自定义,屏蔽底层驱动差异
- MetaObject 封装操作语义:不直接操作 Field,而是通过 MetaObject.setValue("address.city", "Beijing") 这种路径式调用,自动处理嵌套、空对象初始化等边界
- 结果集处理器可插拔:DefaultResultSetHandler 是默认实现,你也可以自定义 ResultSetHandler 替换它,彻底接管反射逻辑(极少需要,但架构上开放)

















