使用Jackson读取CSV文件时遇到getResource返回null异常问题
classLoader.getResource(...) cannot be null的异常 这个问题我之前处理过,本质是资源路径配置错误加上资源加载方式的不当使用导致的,咱们一步步来解决:
一、为什么会出现这个异常?
javaClass.getResource()是从JVM的**类路径(classpath)**中加载资源,而你传入的路径/src/integTest/kotlin/testdata/testCase.csv是源码目录的物理路径——这个路径在项目编译后根本不会存在于classpath里,所以getResource自然返回null。
另外,测试资源(集成测试用的)应该放在专门的资源目录,而不是源码目录(kotlin/java目录)下,否则编译构建工具(比如Gradle/Maven)不会把它复制到classpath中。
二、第一步:调整资源文件的存放位置
把你的testCase.csv移动到集成测试的资源目录:
- 如果是Gradle项目:
src/integTest/resources/testdata/testCase.csv(如果需要分类,可建testdata子目录) - 如果是Maven项目:
src/test/resources/testdata/testCase.csv(Maven集成测试通常用Failsafe插件,资源目录仍为test/resources)
这样构建工具会自动把这个文件复制到编译后的classpath中。
三、第二步:修正资源路径
初始化CsvReader时传入的路径应该改为classpath中的相对路径:
// 如果放在resources/testdata下 var FILE_NAME: String = "/testdata/testCase.csv" // 如果直接放在resources根目录,就是"/testCase.csv"
注意:路径开头的/表示从classpath的根目录开始查找,不带/则从当前类所在的包路径开始查找,这里用绝对路径更稳妥。
四、第三步:优化资源加载方式(避免File的坑)
直接用getResource().file转成File有隐患:如果项目打包成Jar包,资源文件在Jar内部,file协议无法访问Jar内的资源,会抛出异常。更好的方式是用getResourceAsStream获取输入流:
修改你的readCsv方法:
fun readCsv() { val mapper = CsvMapper() val schema = mapper.schemaFor(testCase.javaClass) val objectReader = mapper.readerFor(testCase.javaClass).with(schema) // 用getResourceAsStream获取输入流,避免File的问题 javaClass.getResourceAsStream(fileName)?.use { inputStream -> inputStream.reader().use { reader -> val mappingIterator: MappingIterator<TestCase> = objectReader.readValues(reader) while (mappingIterator.hasNext()) { println(mappingIterator.next()) } } } ?: throw IllegalStateException("Cannot find resource file: $fileName") }
这里用了Kotlin的use函数做自动资源管理,确保流和阅读器会被正确关闭,同时增加了空判断,抛出更清晰的异常信息。
额外排查点
如果还是出现null,检查这几点:
- 确认构建工具(Gradle/Maven)已经把资源文件复制到了编译目录(比如
build/resources/integTest) - 检查文件名/路径的大小写:Windows系统不敏感,但Linux/Mac是大小写敏感的,比如
TestCase.csv和testCase.csv是不同的 - 确认
fileName的开头有没有多余的斜杠或者缺少斜杠
内容的提问来源于stack exchange,提问作者Xaxxus

