运行时报错,日志里直接告知你找不到 Mapper 的 XML,MyBatis 抛出 Invalid bound statement,程序启动挂在那儿。看着一堆配置没毛病,代码也对,可是就是报 FileNotFoundException——大多数时候问题不是你 SQL 写错了,而是 Maven 把文件放错了地方。
先说发生了什么:一个典型场景是把 Mapper 的 XML 和接口放在同一个包里,习惯上会把 UserMapper.java 和 UserMapper.xml 都放到
src/main/java/com/demo/mapper 下。开发时在 IDE 里能看到 XML,一切正常;但是打包后运行,程序提示找不到 mapper 配置。翻查 target、解压 JAR,发现 JAR 里根本没有这个 XML。为什么会这样?缘由要从 Maven 的“默认搬运规则”说起。
Maven 打包时做的事情并不复杂,它把源码编译、资源整理,然后把结果合并进最终的包。按常见流程,执行 mvn package 会把 src/main/java 下的 .java 编译成 .class,放到 target/classes 对应的包目录;而 src/main/resources 目录里的文件会被原样复制到 target/classes 的根目录下。测试代码、测试资源都只在测试阶段使用,压根不会进入最终的 JAR。换句话说,打包后的运行时视角就是把编译后的 class 文件和 resources 的内容一起放在同一层次去运行,classpath 指向的就是这个合并后的根目录(在 IDE 环境下一般是 target/classes,在 JAR 里就是包的根)。
基于上面这套行为模式,有三条“铁律”值得记住:Java 源文件会被 javac 编译成字节码,包路径会映射成目录路径;src/main/resources 的内容会被直接放在打包输出的根下,与 class 文件并列;src/test 下的文件不会出目前生产包里。理解这三点,许多疑问就能迎刃而解。
回到那个出错的例子:你把 UserMapper.xml 放进了
src/main/java/com/demo/mapper,Maven 的默认策略是只在 src/main/java 下找 .java 文件进行编译,非 .java 的文件不会被当作资源复制到输出目录。结果是 class 有了,XML 没了,MyBatis 在运行时去查找 mapper 配置,按约定去 classpath 根或 mapper 子路径找不到,报出 Invalid bound statement 或者直接 FileNotFoundException。这就是为什么许多人会遇到“代码看着没问题,运行却提示没找到资源”的根源。
要解决,有两条常见路子。把 XML 放回 src/main/resources 下合适的相对路径里,让它随资源一起打包进去——这是最简单直接的做法。另一条是在 pom.xml 里显式告知 Maven 把 src/main/java 中的 XML 当作资源复制到输出目录,配置示例如下:
src/main/java
**/*.xml
src/main/resources
把这段放到 pom 的 build 节点下,mvn package 后,src/main/java 下符合条件的 XML 会被拷贝到 target/classes,与编译后的 class 同层,打包进 JAR。注意别想当然只靠 IDE 的显示;IDE 里看得到文件并不代表它会被打包进最终产物。
再补充几个细节,免得后来再被坑。Mapper 的相对路径在运行时是按 classpath 来解析的,也就是说
classpath:mapper/UserMapper.xml 实际上就是去 JAR 根目录或 target/classes 根目录找 mapper/UserMapper.xml。所以把资源放在哪里,运行时就会在哪里找。另一个常见误解是以为把 XML 与接口放在同一目录便于管理,结果忘记了构建工具是按目录类型来处理内容的,不是按语义随意搬运的。
验证办法也很简单:先 mvn package,找到 target 下生成的 JAR,解压查看里面的目录结构,或者直接查看 target/classes 的内容。你会看到编译后的 com/demo/User.class 在对应目录里,resources 下的 application.yml 或 mybatis-config.xml 则在根目录。把 mapper 放错地方的 JAR 里实则根本就没有那个 XML。亲自去看一遍,印象会更深,下一次就不会再踩同样的坑了。
另一个容易混淆的点是大小写和路径一致性。打包后文件名的大小写和原始文件名要一致;有些平台对大小写敏感,运行环境和开发环境不一致时,这类问题会被放大。还有,MyBatis 的配置路径往往写成 classpath:xxx,记住这里的 classpath 指向的是打包后的“合并根目录”,不是源码目录。
最后说两句个人感受:许多人把这些当作黑箱,遇到问题就认为框架有问题。实则弄清楚 Maven 的搬运规则后,许多“莫名其妙”的错误都能被快速定位。实操一下,把 JAR 解压看看,问题一般一目了然。