这个报错我在好几个用 Spring Boot + MyBatis 搭管理后台的项目里都遇到过,最近一次是在跑若依(RuoYi)二开项目的时候——登录正常,页面也加载出来了,一点“参数设置”就抛异常,控制台第一行写着:
code复制org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ruoyi.system.mapper.SysConfigMapper.selectConfigList
这句话翻译成大白话就是:MyBatis 在执行 SysConfigMapper.selectConfigList() 这个方法时,在它自己的“语句注册表”里找不到对应的 SQL。注意这跟你 SQL 写没写对没有直接关系,不管 SQL 里放了什么东西,MyBatis 连这个“语句”都没登记上,后面自然没法执行。这也是为什么新手刚接触这块时会非常困惑:代码明明从仓库拉下来了,接口没改过,XML 也摆在资源目录里,凭什么说 not found?
这类问题本质上是 Spring Boot + MyBatis 项目里的“高频经典病”,搜索热词里能同时出现 SprintBootException、BindingException 和 invalid bound statement not found,本身就说明很多人踩过。标题里那个 SprintBootException 是拼写变形,实际要处理的还是 Spring Boot 应用里 MyBatis 的绑定异常。而报错尾缀拖着一串 com.ruoyi.system.mapper.SysConfigMapper,恰好是若依这类多模块框架里最有代表性的场景。下面我会从报错原理开始,把常见成因一层层剥开,再按实际排查顺序给出可复现的解决办法,同时对 SysConfigMapper 这个具体报错做一次完整复盘。无论是第一次写 Spring Boot 的初学者,还是被多模块项目反复折腾过的后端开发,都可以照着这个思路快速定位问题。
1. 先把报错读明白:Invalid bound statement 到底在说什么
1.1 从异常堆栈看它发生在哪一步
只贴第一行异常往往不够,完整的堆栈里最有价值的信息其实是方法调用链。一个典型的异常开头长这样:
code复制org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ruoyi.system.mapper.SysConfigMapper.selectConfigList
at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:227)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:49)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:63)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
at com.sun.proxy.$ProxyXXX.selectConfigList(Unknown Source)
...
看到 MapperProxy.invoke 这层就很清楚了:调用方拿到的其实是一个 MyBatis 为 Mapper 接口生成的动态代理对象。方法被调用时,代理会先去做一次“翻译”,把接口方法翻译成 MyBatis 内部真正要执行的 statement。如果翻译失败,就抛 BindingException。再往下看,异常出自 MapperMethod$SqlCommand 的构造方法,这个构造过程要做的事就是从全局配置里反查一个 MappedStatement。你可以把 MappedStatement 理解成一条“已经解析好、可以在数据库上执行”的 SQL 指令,它包含 SQL 文本、参数映射、返回值映射、缓存策略等一堆信息。没有它,MyBatis 根本没有执行依据。
所以 Invalid bound statement not found 的核心意思就是:mappedStatements 这个大字典里,没有 com.ruoyi.system.mapper.SysConfigMapper.selectConfigList 这个 key。查不到 key,自然构造不出执行命令。
1.2 为什么启动时不报、调用时才炸
很多人在这时候会有一个巨大的疑问:如果是 Mapper XML 没加载,那为什么 Spring Boot 启动阶段不直接失败?项目照样起来,登录也正常,偏偏点某个功能才把这个异常炸出来。这个疑问背后是 MyBatis 和 Spring 协作时的一个“懒惰”机制。
当 Spring 容器扫描到 Mapper 接口并注册 Bean 时,它做的只是生成一个代理对象放进了容器。这个阶段会检查接口能不能被代理、有没有重复声明等问题,但不会去确保接口里的每个方法都能找到对应 SQL。换句话说,容器只知道 SysConfigMapper 是可用 Bean,不知道 selectConfigList() 背后有没有 SQL。等你真正调用 selectConfigList() 的时候,代理才去全局配置里找 statem
