DEBUG.WIKI
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →

MyBatis 扫描 mapper 报路径 normalized to null,检查 DefaultVFS

Tomcat 9.0.118、10.1.55 下,DefaultVFS 可能把 class 内容当目录条目。按堆栈定位扫描入口,用显式 mapper 注册避开该扫描,不关闭容器校验。

应用创建 SqlSessionFactory 时失败,堆栈经过 org.apache.ibatis.io.DefaultVFS.list、ResolverUtil.find,最里面的异常类似:

java.lang.IllegalArgumentException: The resource path [...] has been normalized to [null] which is not valid

路径里还夹着 .class/ 以及看似乱码的类名、字段名。若刚升级到 Tomcat 9.0.118 或 10.1.55,又通过 MyBatis 的 package 配置扫描 mapper,需要检查 DefaultVFS 是否把 class 文件的内容当成目录条目继续查找。

这条失败发生在类路径资源发现阶段。数据库换一个驱动、修改 SQL 或删除同名表,都不能修正已经拼错的资源路径。

看清错误路径中的内容从哪里来

DefaultVFS 会尝试读取资源流,将每一行当作可能的子资源,再检查它是否存在。正式 MyBatis 3.5.19 的该分支捕获了 InvalidPathException 与 FileSystemException,但容器资源校验抛出的普通 IllegalArgumentException 可以继续向外传播。

当递归走到 class 文件时,二进制字节可能被当作这些“行”。于是异常路径出现类字节码中的符号,甚至空字节,随后被 Tomcat 拒绝。不要先把它判成源文件编码问题,也不要为了通过启动关闭容器的路径合法性检查。

从完整堆栈判断究竟是哪一项包扫描。除了 mapper,typeAliases、typeHandlers 或集成层自动扫描也可能触发资源发现;只改一个不在失败路径上的设置,异常还会存在。

对可枚举的 mapper 使用显式注册

如果故障确实来自 MyBatis XML 中的 mapper 包扫描,而且接口数量可控,可以把这一入口改为逐个注册。原配置例如:

<mappers>
  <package name="com.example.data"/>
</mappers>

改为真实 mapper 接口的完整类名,例如:

<mappers>
  <mapper class="com.example.data.OrderMapper"/>
  <mapper class="com.example.data.CustomerMapper"/>
</mappers>

示例类名要替换为应用已有接口,不是新增两份空接口。显式 class 分支直接加载对应接口并注册,不再为这个 mapper 入口遍历整个包。原注解 SQL、同路径关联的 mapper XML、namespace 以及参数和返回类型仍需完整保留。

若项目原本按 resource 注册 XML,也可以继续采用明确的 XML 资源路径;一个 mapper 不要同时通过多条重复入口注册。Spring 集成层若仍有 @MapperScan 或自动扫描,还要审查那份实际配置,不能改完一份未被加载的 mybatis-config 就宣称已绕开扫描。

typeAliases 的包扫描若也触发同样错误,可改为明确的 typeAlias 项,或在映射里使用完整类型名。不要删除别名之后留下无法解析的 resultType。这个办法是收窄资源发现入口,不是给 DefaultVFS 本身打补丁。

保留容器版本,核对完整映射集合

仅因旧 Tomcat 没触发异常就退回容器,会把容器更新一起撤掉。优先评估显式注册或经审定的 VFS 实现,不复制一份仅按 .class 后缀跳过的补丁:资源可能位于 JAR 或不同容器协议中,后缀过滤不等于完整区分文件与目录。

截至 2026 年 10 月 23 日,已合并并建议验证的 MyBatis 修正指向 3.6.0-SNAPSHOT;不能把它写成已经由 3.5.19 正式包提供的修复。采用后续版本时应核对正式发行内容和实际加载的 JAR。

调整后先检查 SqlSessionFactory 能建立,再对照原清单确认所有 mapper、别名与 typeHandler 都已注册,执行已有的受控查询回归。启动成功但部分接口转而报 Invalid bound statement,通常意味着注册或 XML 加载遗漏,不能算恢复。保留原扫描清单和配置差异;回退时恢复配套配置,不留下半份显式清单。