MyBatis通过全限定接口名+方法名构成的唯一statementId(如com.example.mapper.UserMapper.selectById)绑定SQL,无论XML还是注解方式均以此ID注册到mappedStatements中,代理执行时据此查找并运行对应SQL。

MyBatis 中接口方法与 XML(或注解)SQL 的关联,并非靠“名称匹配”或“反射调用时自动绑定”,而是依赖于 全限定接口名 + 方法名 构成的唯一标识符(即 MappedStatement 的 id),在启动时完成注册与映射绑定。
接口本身不包含 SQL,只是逻辑契约
定义一个 Mapper 接口(如 UserMapper)时,它没有实现类,也不含 SQL。MyBatis 通过 JDK 动态代理为该接口生成代理对象。调用 userMapper.selectById(1) 时,代理实际查找的是名为 com.example.mapper.UserMapper.selectById 的 MappedStatement,并执行其封装的 SQL 和参数处理逻辑。
这个完整 id 是 MyBatis 内部定位 SQL 的唯一钥匙——无论 SQL 来自 XML 还是注解,都必须和这个 id 对应上。
@Select 等注解如何被识别并注册为 MappedStatement
当使用注解方式(如 @Select("SELECT * FROM user WHERE id = #{id}"))时,MyBatis 在扫描 Mapper 接口阶段(通常在 MapperRegistry.addMapper() 或 MapperAnnotationBuilder 解析过程中)会:
- 读取接口的全限定名(如
com.example.mapper.UserMapper) - 遍历每个方法,获取方法名(如
selectById) - 拼接出 statementId:
com.example.mapper.UserMapper.selectById - 解析注解中的 SQL、参数类型、结果映射等元数据,构建
MappedStatement对象 - 将该
MappedStatement注册进Configuration.mappedStatements映射表中
后续执行时,代理对象就靠这个 id 去查表,拿到对应 SQL 执行器、参数处理器、结果处理器等完整执行链。
XML 映射文件如何与接口方法建立同一套 id 规则
XML 方式下,<mapper namespace="com.example.mapper.UserMapper"> 指定了接口全限定名;每个 <select id="selectById"> 的 id 属性就是方法名。MyBatis 启动时解析 XML,同样拼出 namespace + "." + id 作为 statementId,并注册到同一张映射表中。
因此,XML 和注解可以混用,只要 statementId 不冲突即可。例如:接口中定义了 selectById 方法,既可以用 @Select 注解,也可以在 XML 中写同名 <select id="selectById">,但二者不能共存(否则启动报错“重复注册”)。
参数绑定与方法签名决定 #{ } 解析逻辑
方法签名(参数个数、类型、是否加 @Param)决定了 MyBatis 如何构建参数对象(ParamMap 或实体类),进而影响 #{id} 这类占位符的取值路径:
- 单个基础类型参数(如
Long id)→ 默认 key 为param1,也可用@Param("id")指定为id - 多个参数 → 必须用
@Param显式命名,否则无法通过名称引用 - 实体类参数 → 直接按属性名取值,如
#{username}对应user.getUsername()
注意:注解里的 SQL 字符串本身不参与方法签名解析,它只负责提供原始 SQL 文本;真正的参数绑定逻辑由 MyBatis 根据方法定义+运行时传参动态完成。
理解这套基于 statementId 的注册-查找机制,就能明白为什么接口方法名必须和 XML id 或注解所在方法严格一致,也能避免常见错误:方法名改了但 XML 没同步、接口类路径变了导致 namespace 失效、或者多个模块引入同名 Mapper 导致 ID 冲突。

















