SpringExtension测试Tomcat+Hibernate Web应用时mappingJarLocations异常
我们在使用spring-test提供的SpringExtension为基于Spring + Hibernate构建的Tomcat Web应用编写集成测试时,遇到了路径解析异常问题。
当前sessionFactory Bean的mappingJarLocations属性配置为/WEB-INF/lib/company-common*.jar,该路径指向的Jar包内存储Hibernate映射文件:
- 生产部署、Eclipse部署到Tomcat的场景下,该配置可正常生效:Servlet容器会自动将
docBasePath拼接至路径前缀,可正常定位到目标Jar包 - 本地、CI环境运行JUnit测试用例时,该路径无法被解析。
此前我们尝试基于官方扩展能力,自定义重写WebTestContextBootstraper、GenricXmlWebContextLoader、XmlWebApplicationContext、WebDelegatingSmartContextLoader适配该场景,但最终未能落地:org.springframework.test.context.web.AbstractGenericWebContextLoader.loadContext(MergedContextConfiguration)是final修饰方法,无法重写,因此无法注入自定义的XmlWebApplicationContext实现。目前采用的临时方案是手动创建应用上下文供测试逻辑调用。
项目结构如下:
Project_WebApp |--src/** |--WebContent/** |--pom.xml
两种常规部署场景的依赖包存储路径:
- 应用打包为
Project_WebApp.war部署时,依赖包位于War包解压后根目录的WEB-INF/lib路径下 - 通过Eclipse将应用部署至Tomcat时,依赖包会被复制到
<Eclipse_Workspace_Dir>/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/Project_WebApp/WEB-INF/lib路径下
两种场景下依赖包均位于<Resource_Base_Path>/WEB-INF/lib路径下,且Resource_Base_Path与Project_WebApp的项目根目录无关联。
待确认两个问题:
- 是否有开发者在类似场景下使用过
SpringExtension?是否有可行的替代实现方案? - 此前尝试将
/WEB-INF/lib/company-common*.jar替换为类路径匹配模式但未能生效,原因是获取到的类路径资源无法匹配对应模式,是否有其他可尝试的解决思路?
方案1:通过Spring Profile隔离测试与生产配置
这是落地成本最低的方案,不需要改动Spring Test核心加载逻辑:
- 生产、Eclipse部署场景对应的
sessionFactory配置,保留原有mappingJarLocations = /WEB-INF/lib/company-common*.jar的配置项,绑定到prod、devprofile生效 - 新增测试环境专属的
sessionFactory配置,绑定到testprofile生效,直接使用mappingLocations或packagesToScan替代mappingJarLocations加载Hibernate映射:如果映射文件在类路径下,直接配置classpath*:com/company/**/*.hbm.xml即可,完全绕开WEB-INF路径的文件系统依赖 - 测试类上添加
@ActiveProfiles("test")激活测试profile,即可正常加载上下文。
方案2:自定义ResourceLoader适配路径解析
如果不想维护两套配置,直接通过@ContextConfiguration指定自定义ContextLoader即可,不需要重写final修饰的loadContext方法:
- 继承
GenericXmlWebContextLoader,重写官方预留的扩展方法customizeContext,在上下文refresh前,给XmlWebApplicationContext注入自定义的Resource解析逻辑:所有以/WEB-INF/lib/开头的资源路径,自动代理为类路径资源匹配 - 测试运行时
company-common*.jar本身就会被Maven/Gradle放到测试类路径下,解析逻辑中遇到对应路径,直接调用PathMatchingResourcePatternResolver使用classpath*:company-common*.jar模式匹配资源即可,完全不需要依赖Web容器的docBase路径 - 该扩展点是Spring专门为上下文自定义场景预留的,不存在版本兼容性问题。
方案3:测试时显式指定Web应用根目录
如果要100%复用生产环境配置不做任何修改,直接通过@WebAppConfiguration指定测试用的Web资源根目录即可:
- 配置maven-dependency-plugin(Gradle环境配置对应依赖复制插件),在测试执行前将
company-common相关Jar包复制到测试构建目录下的固定路径,比如target/test-webapp/WEB-INF/lib/ - 测试类上配置
@WebAppConfiguration("target/test-webapp"),指定Spring Test加载Web上下文时以该目录为资源根,原有/WEB-INF/lib/company-common*.jar路径即可被正常解析 - 该方式和生产环境的配置加载逻辑完全一致,不需要修改任何Bean定义,仅需调整构建插件的测试阶段配置。
之前类路径匹配模式不生效的核心原因:Hibernate的mappingJarLocations本身只接收file://协议的文件系统路径,不直接识别Spring的classpath*:通配符,直接替换路径字符串的话,Hibernate会把类路径模式当成普通磁盘路径处理,自然匹配不到资源。如果要走类路径匹配,先通过Spring的PathMatchingResourcePatternResolver将匹配到的Jar资源解析为URL对象,再转换成对应的文件路径传给mappingJarLocations即可正常识别。
内容的提问来源于stack exchange,提问作者Ganapathi Basimsetti

