1. 这个报错到底在说什么:从"Input length = 1"读懂Maven的沉默抗议
第一次看到Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources后面跟着一句莫名其妙的Input length = 1,我盯着屏幕愣了大概半分钟。这行日志的诡异之处在于:它既没有告诉你哪个文件出了问题,也没有堆栈信息指向具体代码行,就干巴巴甩出一句"输入长度为1",然后构建就挂了。如果你正在赶进度,这种报错绝对能让人血压飙升。
先把结论摆在前面:这个报错99%的情况是资源文件编码问题导致的,具体来说,是maven-resources-plugin在读取某个资源文件时,遇到了它无法用当前配置的字符集解析的字节序列。Input length = 1是 Java 的CharsetDecoder在解码时抛出的MalformedInputException的经典消息,意思是"我拿到了一段长度为1的输入,但它不符合我当前字符集的编码规则,我解不了"。
为什么这个报错这么难排查?因为 Maven 在资源处理阶段是批量扫描src/main/resources和src/test/resources目录的,它不会告诉你具体是哪个文件触发了异常。你只能靠"二分法"或者"编码检测"来定位。而且这个报错在不同 Maven 版本、不同 JDK 版本下的表现还不完全一样,有时候会带上文件名,有时候就是光秃秃一句话。
这篇文章我会把这个问题彻底拆开讲清楚:从maven-resources-plugin的工作机制,到Input length = 1的底层触发原理,再到完整的排查链路和多种修复方案。不管你是刚接触 Maven 的新手,还是被这个报错折磨过的老手,都能从里面找到可以直接抄作业的步骤。
提示:如果你现在正卡在这个报错上,先别急着改
pom.xml的编码配置。很多时候问题不在配置本身,而在于某个资源文件里混入了"脏字节"。盲目改配置可能掩盖问题,后面还会以更诡异的方式爆出来。
2. maven-resources-plugin 在构建流程里到底干了什么
2.1 资源处理阶段的位置与职责
要理解这个报错,得先搞清楚maven-resources-plugin在整个 Maven 生命周期里扮演什么角色。Maven 的默认生命周期里,process-resources阶段绑定的是resources:resources目标,process-test-resources阶段绑定的是resources:testResources。这两个目标做的事情很朴素:把src/main/resources和src/test/resources目录下的文件,复制到target/classes和target/test-classes目录里。
听起来很简单对吧?但"复制"这个动作在 Maven 里不是简单的文件拷贝。它涉及到几个关键处理:
- 变量替换(filtering):如果开启了 filtering,Maven 会把资源文件里的
${...}占位符替换成pom.xml里定义的属性值。这个过程需要把文件内容读成字符串,替换后再写回去。 - 编码转换:读取文件时用一套字符集,写入时用另一套字符集。如果两边不一致,或者源文件的字节序列不符合声明的字符集,就会抛异常。
- 文件过滤:根据
includes和excludes配置决定哪些文件需要处理,哪些直接跳过。
Input length = 1这个报错,就发生在"读取文件"这一步。maven-resources-plugin底层用的是 Plexus Utils 的FileUtils.fileRead()或者类似的 IO 工具,这些工具在读取文件时会用InputStreamReader配合指定的Charset来解码字节流。当字节流里出现了不符合该字符集规则的字节序列时,CharsetDecoder就会抛出MalformedInputException,而Input length = 1正是这个异常的消息内容。
2.2 为什么偏偏是"length = 1"
这里有个很多人困惑的点:为什么是"长度为1"?难道文件里只有一个字符有问题?
其实Input length = 1里的"1"不是指文件里只有一个坏字节,而是指CharsetDecoder在解码过程中,当前这一步拿到的输入缓冲区里,从当前位置开始有1个字节无法被解码。换句话说,解码器是逐段处理字节的,当它发现某个位置的字节序列不符合规则时,就会报告"我这里拿到了长度为1的输入,但它不合法"。
举个具体的例子。假设你的文件是 UTF-8 编码的,但pom.xml里配置的project.build.sourceEncoding是GBK。UTF-8 的中文字符通常是3个字节,比如"中"是E4 B8 AD。当解码器用 GBK 去解这3个字节时,GBK 是双字节编码,它会把E4 B8当成一个字符,剩下AD单独一个字节。这个AD在 GBK 里可能不是一个合法的首字节,解码器就会报Input length = 1。
反过来也一样。如果文件是 GBK 编码,配置是 UTF-8,GBK 的中文字符是2个字节,UTF-8 解码器看到这两个字节时,可能第一个字节是合法的 UTF-8 首字节,但第二个字节不符合后续字节的规则,同样会报错。
还有一种常见情况:文件本身是 UTF-8 无 BOM 的,但里面混入了从 Windows 记事本保存时带上的 BOM 头(EF BB BF),或者从某些系统复制粘贴时带入了不可见的控制字符。这些"脏字节"在特定编码下就会触发解码异常。
2.3 报错信息为什么经常"缺胳膊少腿"
正常情况下,MalformedInputException应该带上出错的位置信息,但maven-resources-plugin在捕获异常后,往往只把异常消息原样往上抛,没有附加文件名和行号。这就导致你看到的日志只有一句Input length = 1,完全不知道是哪个文件惹的祸。
更麻烦的是,Maven 的日志级别和插件版本会影响报错的详细程度。有些版本的maven-resources-plugin会在报错前打印正在处理的文件名,有些版本则什么都不打。如果你用的是 3.2.0 这个版本,恰好它的日志输出比较"吝啬",排查起来就更费劲。
注意:不要指望通过加
-X参数就能直接看到出错文件名。-X会输出大量调试信息,但资源处理阶段的文件级日志往往还是不够明确。真正有效的定位手段是下面要讲的"二分排查法"和"编码检测法"。
3. 定位问题文件:一套可复现的排查链路
3.1 第一步:确认报错发生在哪个资源目录
在动手排查之前,先缩小范围。maven-resources-plugin会处理两个目录:src/main/resources和src/test/resources。报错可能发生在任意一个。
你可以先执行一次只处理主资源的构建:
mvn process-resources -X 2>&1 | grep -A 5 "maven-resources-plugin"如果报错出现在这一步,问题就在src/main/resources。如果这一步通过了,再执行:
mvn process-test-resources -X 2>&1 | grep -A 5 "maven-resources-plugin"这样就能确定是主资源还是测试资源出了问题。别小看这一步,很多人一上来就全局搜索,结果在错误的方向上浪费了大量时间。
3.2 第二步:用二分法锁定具体文件
确定目录后,接下来要找出具体是哪个文件。最笨但最有效的方法是二分法:
- 把
src/main/resources下的文件分成两半,暂时把其中一半移到临时目录。 - 执行
mvn process-resources,看是否还报错。 - 如果还报错,说明问题在剩下的一半里;如果不报错了,说明问题在被移走的那一半里。
- 对有问题的那一半重复上述步骤,直到锁定单个文件。
这个方法听起来很土,但在没有明确日志的情况下,它是最高效的。一个中等规模的项目,资源文件通常也就几十到几百个,二分法最多七八轮就能定位到具体文件。
如果你嫌手动移动文件太麻烦,可以写个简单的脚本辅助:
#!/bin/bash # 二分排查脚本示例 RESOURCE_DIR="src/main/resources" TEMP_DIR="/tmp/resource_backup" # 备份所有资源 cp -r "$RESOURCE_DIR" "$TEMP_DIR" # 这里需要你手动配合,每次移动一半文件后运行 mvn process-resources # 根据结果决定保留哪一半当然,更省事的办法是用下面的编码检测法直接扫描。
3.3 第三步:用 file 命令和 iconv 快速检测编码
在 Linux 或 macOS 上,file命令可以快速查看文件的编码信息:
file -i src/main/resources/*.properties file -i src/main/resources/*.xml file -i src/main/resources/**/*.jsonfile -i会输出类似charset=utf-8或charset=iso-8859-1的结果。如果你发现某个文件显示的不是utf-8,而你的项目配置是 UTF-8,那它很可能就是罪魁祸首。
更精确的检测可以用iconv尝试转换:
iconv -f UTF-8 -t UTF-8 src/main/resources/config.properties -o /dev/null如果文件不是合法的 UTF-8,iconv会报错并指出出错的位置。这个命令的好处是它能告诉你具体在第几行第几列出了问题,比 Maven 的报错信息有用得多。
在 Windows 上,可以用 PowerShell 来检测:
Get-Content -Path "src\main\resources\config.properties" -Encoding UTF8 -ErrorAction Stop如果文件不是 UTF-8,PowerShell 会抛出异常。
3.4 第四步:检查文件里的"隐形杀手"
有些文件用file命令检测显示是 UTF-8,但依然会触发Input length = 1。这种情况通常是文件里混入了不可见字符。常见的"隐形杀手"包括:
- BOM 头:Windows 记事本保存 UTF-8 文件时会自动加上
EF BB BF。虽然 BOM 本身是合法的 UTF-8 字节序列,但某些工具在处理时会把 BOM 当成内容的一部分,导致后续解析出错。 - 零宽字符:从网页或聊天工具复制代码时,经常会带入零宽空格(
E2 80 8B)、零宽非连接符(E2 80 8C)等不可见字符。 - 控制字符:比如
0x00到0x1F之间的某些控制字符,在 XML 和 properties 文件里是非法的。 - 混合换行符:Windows 的
\r\n和 Unix 的\n混用,虽然通常不会导致编码错误,但在某些解析场景下会引发问题。
检测这些隐形字符,可以用hexdump或xxd查看文件的原始字节:
xxd src/main/resources/config.properties | head -20如果文件开头出现efbb bf,那就是 BOM。如果中间出现e280 8b之类的序列,那就是零宽字符。
用grep也可以快速查找 BOM:
grep -rl $'\xEF\xBB\xBF' src/main/resources/这个命令会列出所有带 BOM 的文件。
4. 修复方案:从根因出发的四种处理策略
4.1 策略一:统一项目编码配置(推荐)
最根本的解决办法是在pom.xml里显式声明编码,并确保所有资源文件都用同一种编码保存。标准的配置如下:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties>这两个属性分别控制源码编译和报告输出的编码。maven-resources-plugin会读取project.build.sourceEncoding作为资源处理的默认编码。
但光配置这个还不够。你还需要在maven-resources-plugin的配置里显式指定编码:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>为什么要两处都配?因为project.build.sourceEncoding是一个"约定",很多插件会读取它,但不是所有插件都严格遵守。在插件自己的configuration里显式指定encoding,是确保万无一失的做法。
配好之后,把所有资源文件统一转成 UTF-8 编码。在 Linux/macOS 上可以用iconv批量转换:
find src/main/resources -type f \( -name "*.properties" -o -name "*.xml" -o -name "*.json" \) -exec sh -c ' for file do iconv -f GBK -t UTF-8 "$file" -o "$file.utf8" 2>/dev/null && mv "$file.utf8" "$file" done ' sh {} +在 Windows 上,可以用 Notepad++ 的"编码"菜单批量转换,或者用 PowerShell 脚本处理。
4.2 策略二:处理 properties 文件的特殊编码规则
这里要特别说一下.properties文件。Java 的Properties类在读取文件时,默认使用 ISO-8859-1 编码,而不是 UTF-8。这是 Java 历史遗留问题,很多人不知道。
这意味着,即使你把maven-resources-plugin的编码配成了 UTF-8,如果.properties文件里有中文,Properties.load()依然可能出问题。正确的做法有两种:
方案A:用 Unicode 转义
把.properties文件里的所有非 ASCII 字符转成\uXXXX形式。比如"中文"转成\u4e2d\u6587。IDEA 和 Eclipse 都有插件可以自动做这个转换。
方案B:用 XML 格式的 properties
Java 支持 XML 格式的 properties 文件,这种格式可以显式声明编码:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <entry key="app.name">我的应用</entry> </properties>然后用Properties.loadFromXML()来读取。这种方式没有编码歧义,但可读性稍差。
方案C:在代码里指定编码读取
如果你能控制读取 properties 的代码,可以用InputStreamReader显式指定 UTF-8:
Properties props = new Properties(); try (InputStreamReader reader = new InputStreamReader( new FileInputStream("config.properties"), StandardCharsets.UTF_8)) { props.load(reader); }这种方式最灵活,但需要改代码,适合新项目。
提示:很多
Input length = 1的案例,根源就是.properties文件里写了中文,而项目编码配置和文件实际编码不一致。优先检查所有.properties文件。
4.3 策略三:清理 BOM 和不可见字符
如果确认是 BOM 或零宽字符导致的问题,需要清理这些"脏字节"。清理 BOM 可以用sed:
# 清理 UTF-8 BOM sed -i '1s/^\xEF\xBB\xBF//' src/main/resources/config.properties批量清理:
find src/main/resources -type f -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;清理零宽字符可以用perl:
perl -i -pe 's/\x{e2}\x{80}\x{8b}//g; s/\x{e2}\x{80}\x{8c}//g' src/main/resources/config.properties在 Windows 上,可以用 Notepad++ 的"显示所有字符"功能查看不可见字符,然后手动删除。或者用 PowerShell:
$content = [System.IO.File]::ReadAllText("config.properties") $content = $content -replace "[\u200B-\u200D\uFEFF]", "" [System.IO.File]::WriteAllText("config.properties", $content, [System.Text.Encoding]::UTF8)清理完之后,建议用xxd再检查一遍文件头,确认 BOM 已经没了。
4.4 策略四:跳过不必要的资源处理
有时候,问题文件其实不需要被maven-resources-plugin处理。比如某些二进制文件、图片、字体文件,它们本来就不是文本,不应该被编码转换。这时候可以通过includes和excludes配置来跳过它们:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <encoding>UTF-8</encoding> <nonFilteredFileExtensions> <nonFilteredFileExtension>jpg</nonFilteredFileExtension> <nonFilteredFileExtension>png</nonFilteredFileExtension> <nonFilteredFileExtension>gif</nonFilteredFileExtension> <nonFilteredFileExtension>pdf</nonFilteredFileExtension> <nonFilteredFileExtension>zip</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin>nonFilteredFileExtensions告诉插件:这些扩展名的文件不要做 filtering,直接二进制拷贝。这样就能避免插件去"读"这些文件的内容,自然也不会触发编码错误。
如果你确定某个目录下的文件完全不需要处理,也可以用excludes:
<configuration> <excludes> <exclude>**/*.bin</exclude> <exclude>templates/**</exclude> </excludes> </configuration>但要注意,excludes掉的文件不会被复制到target/classes,如果运行时需要这些文件,就不能排除。
5. 几个容易踩的坑和实测经验
5.1 改了 pom.xml 但问题依旧:缓存和 IDE 的干扰
我遇到过好几次这样的情况:明明在pom.xml里配好了 UTF-8,文件也用iconv转过了,但mvn clean package还是报Input length = 1。后来发现是 IDE 的缓存或者 Maven 的本地仓库在捣乱。
第一个坑:IDE 的编码设置覆盖了 Maven 配置。IntelliJ IDEA 有自己的文件编码设置(Settings → Editor → File Encodings),如果这里配的是 GBK,而文件实际是 UTF-8,IDE 在保存文件时可能会"自作主张"地转换编码。解决办法是把 IDE 的编码也统一成 UTF-8,并且勾选"Transparent native-to-ascii conversion"(针对 properties 文件)。
第二个坑:Maven 本地仓库里的旧插件缓存。有时候maven-resources-plugin的旧版本被缓存了,即使你改了pom.xml里的版本号,Maven 还是用旧的。解决办法是删掉本地仓库里对应的插件目录:
rm -rf ~/.m2/repository/org/apache/maven/plugins/maven-resources-plugin/然后重新构建,让 Maven 重新下载。
第三个坑:target目录没清理。有时候target/classes里残留了上一次构建的旧文件,导致问题看起来"阴魂不散"。养成习惯:改完配置先mvn clean,再mvn package。
5.2 多模块项目里的编码传递问题
在多模块 Maven 项目里,父pom.xml里配的project.build.sourceEncoding不一定会自动传递给所有子模块。特别是当子模块有自己的<properties>覆盖时,编码配置可能就丢了。
我的做法是在父pom.xml里用<pluginManagement>统一管理maven-resources-plugin的配置:
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build>然后在每个子模块里只需要声明插件(不需要重复配置):
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> </plugin> </plugins> </build>这样能保证所有子模块用同一套编码配置,避免"这个模块好、那个模块坏"的诡异现象。
5.3 从 GBK 迁移到 UTF-8 的完整操作清单
如果你的项目是从 GBK 迁移到 UTF-8,下面这份清单可以帮你少走弯路:
| 步骤 | 操作 | 验证方式 |
|---|---|---|
| 1 | 备份整个项目 | 确认备份目录存在 |
| 2 | 修改父 pom 的 sourceEncoding 为 UTF-8 | mvn help:effective-pom查看 |
| 3 | 用 iconv 批量转换资源文件 | file -i确认编码 |
| 4 | 清理所有 BOM | grep -rl $'\xEF\xBB\xBF'无输出 |
| 5 | 转换 Java 源码文件 | IDE 批量转换或 iconv |
| 6 | 清理 target 和本地仓库缓存 | mvn clean |
| 7 | 全量构建验证 | mvn clean package成功 |
| 8 | 运行测试确认功能正常 | 测试用例全部通过 |
这份清单看起来简单,但每一步都有细节。比如第3步,iconv转换时如果遇到无法转换的字符,会直接报错退出。这时候需要加//IGNORE参数忽略无法转换的字符,但忽略之后要人工检查是否有内容丢失。
第5步转换 Java 源码时,要注意源码里的中文字符串常量。如果源码是 GBK,直接转 UTF-8 后,字符串常量会变成乱码。正确的做法是先用 IDE 的"Convert encoding"功能,它会同时处理文件编码和字符串内容。
5.4 一个真实的排查案例
之前有个项目,mvn clean package在本地开发机上一直报Input length = 1,但同事的机器上却能正常构建。我们对比了两边的环境,JDK 版本、Maven 版本、pom.xml都一模一样。
最后发现问题出在一个.properties文件上。这个文件里有一行注释,是某位同事从网页上复制配置项时带进来的,注释末尾有一个零宽空格。在同事的机器上,这个零宽空格恰好被某个编辑器"吃掉"了,所以构建正常。而在我的机器上,文件是从 Git 仓库直接拉下来的,零宽空格原样保留,就触发了编码错误。
用xxd查看那个文件,在注释行末尾看到了e2 80 8b三个字节。删掉之后,构建立刻恢复正常。
这个案例给我的教训是:不要相信"在我机器上是好的"这种话。编码问题往往和文件在传输、复制、保存过程中的细微差异有关。遇到这类问题,第一件事是用xxd看原始字节,而不是靠肉眼观察。
6. 预防措施:让这个报错不再出现
6.1 在 CI 流程里加入编码检查
与其等构建时报错,不如在代码提交阶段就发现问题。可以在 CI 流程里加一个编码检查步骤:
#!/bin/bash # 检查所有资源文件是否为合法 UTF-8 find src/main/resources -type f \( -name "*.properties" -o -name "*.xml" -o -name "*.json" -o -name "*.yml" \) | while read file; do if ! iconv -f UTF-8 -t UTF-8 "$file" -o /dev/null 2>/dev/null; then echo "编码错误: $file" exit 1 fi done echo "所有资源文件编码检查通过"这个脚本会遍历所有文本资源文件,用iconv验证它们是否是合法 UTF-8。如果有问题,CI 会直接失败,阻止有问题的代码合并。
还可以加上 BOM 检查:
if grep -rl $'\xEF\xBB\xBF' src/main/resources/; then echo "发现 BOM 文件,请清理" exit 1 fi6.2 统一团队的文件保存规范
编码问题的根源往往是"人"的问题,而不是"技术"的问题。不同的人用不同的编辑器,保存文件时用了不同的编码,问题就来了。
我的建议是在项目根目录放一个.editorconfig文件:
root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{properties,xml,json,yml,yaml}] charset = utf-8.editorconfig被主流 IDE 和编辑器支持,能强制统一编码、换行符、缩进等格式。虽然它不能100%杜绝问题,但能大幅降低"每个人保存的文件都不一样"的概率。
另外,在项目的 README 或者开发规范文档里明确写一句:"所有文本文件必须使用 UTF-8 无 BOM 编码保存。"这句话看起来是废话,但真到了出问题的时候,它能帮你省下几个小时的排查时间。
6.3 升级 maven-resources-plugin 的版本
maven-resources-plugin3.2.0 是2020年发布的版本,虽然不算老,但后续版本在错误提示方面有所改进。比如 3.3.0 之后的版本,在遇到编码错误时会更明确地指出是哪个文件出了问题。
如果你的项目没有特殊原因锁定在 3.2.0,可以考虑升级到更新的版本:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> </plugin>升级前建议先在小范围测试,确认没有兼容性问题。大多数情况下,maven-resources-plugin的版本升级是无痛的,因为它做的事情很单一。
6.4 给 properties 文件加一道保险
对于.properties文件,我习惯在项目里加一个自定义的编码检查工具类,在应用启动时扫描所有 properties 文件,确认它们能被正确加载。这样即使构建阶段漏过了问题,启动阶段也能快速发现。
public class PropertiesEncodingChecker { public static void checkAll(Path resourceDir) throws IOException { Files.walk(resourceDir) .filter(p -> p.toString().endsWith(".properties")) .forEach(p -> { try (InputStreamReader reader = new InputStreamReader( Files.newInputStream(p), StandardCharsets.UTF_8)) { new Properties().load(reader); } catch (IOException e) { throw new RuntimeException("Properties 文件编码错误: " + p, e); } }); } }这个工具类可以在单元测试里调用,确保所有 properties 文件都是合法 UTF-8。虽然多写了几行代码,但比起在生产环境发现配置乱码,这点投入完全值得。
7. 写在最后:编码问题没有捷径
Input length = 1这个报错,说到底是一个"信息不足"的问题。Maven 没有告诉你哪个文件出了问题,你只能靠自己的排查手段去定位。我见过有人因为这个报错重装了 JDK,有人换了 Maven 版本,有人把整个项目重新 clone 了一遍——这些操作偶尔能"碰巧"解决问题,但大多数时候只是浪费时间。
真正有效的做法是:先定位文件,再分析字节,最后统一编码。这三步走下来,90%以上的编码问题都能解决。剩下的10%,通常是文件里混入了极其隐蔽的不可见字符,需要用xxd逐字节检查。
我在实际项目里养成了一个习惯:每次新建资源文件后,先用file -i确认编码,再用xxd看一眼文件头有没有 BOM。这个习惯花不了几秒钟,但能避免后面几个小时的排查。编码问题就是这样,预防的成本远低于修复的成本。
如果你现在正被这个报错卡住,不妨按这篇文章的排查链路走一遍。先从mvn process-resources缩小范围,再用二分法锁定文件,然后用iconv或xxd分析字节,最后统一编码配置。整个过程可能需要半小时到一小时,但比起盲目尝试各种"偏方",这条路是最稳的。