
有没有遇到过这种场景你在 IDEA 里刷新了一下 Maven 项目或者执行mvn clean package结果控制台直接甩出一行红色报错Fatal error compiling: 错误: 不支持发行版本 5。我第一次碰到这个报错时真的一头雾水代码一行没改早上还能编译下午就挂掉了。后来把 Maven、JDK、IDE 的版本关系捋清楚了才发现这类问题本质上就一句话当前用来编译的 JDK 不认识你在项目里指定的编译版本。这篇文章我会从报错原理、触发场景、解决方案到完整排查流程一次性讲透。不管你是刚入门的 Java 开发还是被老项目折腾得不行的老手看完应该都能少走很多弯路。1. 错误是怎么来的先理解 Java 编译版本机制1.1 从报错信息看问题本质javac编译 Java 文件时有两个非常重要的参数-source和-target。-source决定编译器接受哪些 Java 语法-target决定生成的class文件目标版本。Maven 在编译时会根据配置把这两个参数传给javac。如果你在pom.xml里写了maven.compiler.source1.8Maven 会把-source 1.8-target 1.8传给javac。一旦javac拿到一个它不认识的版本号它就会抛出Fatal error compiling: 错误: 不支持发行版本 xx。注意这里的关键词是“不支持”它既可能指版本太新也可能指版本太老。比如你本机安装的是 JDK 17Maven 运行在 JDK 17 上但项目里依然写着source/target 1.5而 JDK 17 的javac早就删除了对1.5的支持于是立刻报错。这就好比你拿着门禁卡去开新公司的门门禁卡的磁条信息已经是十几年前的旧格式机器读出来了但不认账自然不让你进。理解了这一点你就会明白为什么很多人一换电脑、一换 JDK老项目就立刻编译失败——不是代码坏了是编译开关和运行 JDK 对不上。1.2 版本体系JDK、source/target/release 的区别很多新手容易把 JDK 版本和编译版本混为一谈。JDK 版本是指你本机安装的开发工具包比如 JDK 8、11、17、21而source、target是javac的两个编译开关。source决定编译时可以用的语法target决定生成class文件的字节码版本。后来 JDK 9 又引入了--release参数它等于把source、target和 API 版本一起锁定更严格也更好用。Maven 项目里最常见的编译版本配置有两种方式。一种是在properties里定义properties maven.compiler.source11/maven.compiler.source maven.compiler.target11/maven.compiler.target /properties另一种就是在maven-compiler-plugin的configuration里写build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source11/source target11/target /configuration /plugin /plugins /build如果项目里什么编译版本都不写Maven 默认使用maven-compiler-plugin内置的默认值很多老版本插件默认是1.5。JDK 8 还能勉强编译JDK 9 以后基本必挂。这也是为什么大家看到“不支持发行版本 5”的频率远高于其他版本号——因为 5 就是那个最老的默认值而你早就换了新版 JDK。1.3 为什么 Maven 会“自作主张”选错版本Maven 本身是 Java 写的它运行在哪个 JDK 上就用哪个 JDK 的javac来编译代码。但项目要编译成哪个目标版本是由pom.xml里的配置决定的。这两套信息各自独立一旦对不上问题就出现了。常见的情况有三种。第一pom.xml里没写编译版本Maven 用默认的1.5遇到新版 JDK 直接报错。第二pom.xml里写了1.8但你本机只有一个 JDK 17绝大多数情况下 JDK 17 还是支持编译到1.8的可如果你写的是1.6甚至更老可能就会“不支持”。第三IDEA 里 Project Structure 配的是 JDK 8但 Maven Runner 的 JRE 选了 JDK 17编译时 Maven 用的是 17 的javac另一方面项目 Language Level 又被 IDE 改来改去最后各种不一致叠加在一起报错就出现了。所以排错的关键就是先搞清楚到底是谁在编译、目标版本是什么、为什么会有冲突。2. 最常见的触发场景哪些项目最容易踩这个坑2.1 场景一换了 JDK 版本老项目直接编译失败这应该是遇到最多的场景。早上还在用 JDK 8 开发下午公司要求统一换 JDK 17或者新电脑默认安装了 JDK 17结果一打开老项目跑mvn clean package红字就出来了。老项目往往用着很老的 Maven 插件版本pom.xml里也没专门配置编译版本所以 Maven 默认按1.5编译。新版 JDK 的javac已经不支持1.5于是结论就是“Fatal error compiling”。这种情况也和项目年龄直接相关。如果你维护的是那种从 2015 年传到今天、换过好几轮人的内部系统pom.xml里可能还留着很多古董配置连maven-compiler-plugin都停在 2.x。你没法指望这种项目自己适配新 JDK必须手动介入。最简单的办法是在pom.xml里把编译版本改成当前 JDK 支持的范围。如果你们业务代码没用特别新的语法改成 8 或 11 通常都能直接编译通过。2.2 场景二IDE 内置 JDK 和 Maven 运行 JDK 不一致IDEA 里跑 Maven 和我们在终端里跑 Maven其实是两套运行环境。终端里取决于JAVA_HOME指向哪个 JDKIDEA 里则要看Settings Build, Execution, Deployment Build Tools Maven Runner JRE这一项。很多人只在 IDEA 里点 Maven 面板执行构建终端里从没跑过所以压根没意识到两边的 JDK 可能不一样。举个例子你的 IDEA 项目 SDK 配置为 JDK 8Maven Runner 的 JRE 却选了 JDK 17pom.xml里写的还是source/target 1.5那编译时 Maven 用 17 的javac看到1.5就不认识。就算你把source/target改成1.8如果 Runner 里的 JRE 一直指向 17通常也能编过但一旦项目里有某些老框架用到了反射或字节码操作运行时的兼容性问题就开始冒出来了。所以我的经验是IDE 里的 Project SDK、Language Level、Maven Runner JRE 这三项最好统一成同一个 JDK不要一边一个版本。2.3 场景三maven-compiler-plugin 版本与 JDK 的兼容问题还有一个经常被忽略的坑maven-compiler-plugin本身版本太老。老版本插件对高版本 JDK 的适配并不好可能会出现一些很隐晦的编译错误。比如插件 2.5.1 时代它内部的默认参数就是 1.5而且它的逻辑不会自动感知当前 JDK 能支持什么版本。后来新版本插件3.8.1 以上才对 JDK 9 做了更好的适配支持--release参数也能识别更高版本的 JDK。所以如果你发现自己的项目里还在用 3.1、3.2 甚至 2.x 的maven-compiler-plugin建议直接升级到 3.8.1 以上我目前常用的稳定版本是 3.11.0。升级插件本身不会对项目功能造成影响但它能让你少踩非常多莫名其妙的坑。配合在pom.xml里显式声明编译版本基本就能把“Fatal error compiling”这类问题连根拔起。3. 解决方法从最快到最彻底3.1 最快直接修改 IDEA 中的 Java Compiler 设置如果你的第一诉求是“赶紧让项目跑起来”那最快的方式是改 IDEA 的编译设置。打开Settings Build, Execution, Deployment Compiler Java Compiler看当前模块的 bytecode version 是多少把它改成和本机 JDK 一致。然后去Project Structure Project把 SDK 和 Language Level 也改成同一版本。改完以后重新加载 Maven 项目很多时候报错就消失了。但这个方法有一个很大的隐患它只是改了你本机 IDE 的配置不会同步到pom.xml。等团队其他人拉取代码依然会报同样的错等你把项目换到 CI 机器上构建CI 上也没有你这份 IDE 设置一样会失败。所以我一般只建议用它来做快速验证比如你不想动pom.xml先试试看项目在某个 JDK 下能不能跑通。最终解决问题还是要靠把编译版本写进构建文件里。3.2 彻底在 pom.xml 里显式声明编译版本最根本的解决办法是在pom.xml里明确告诉 Maven这个项目要用哪个版本的 Java 语法要生成哪个版本的字节码。最简单的写法就是在properties里加两行properties maven.compiler.source11/maven.compiler.source maven.compiler.target11/maven.compiler.target /properties如果你需要更明确地控制插件版本也可以把maven-compiler-plugin的配置完整写出来build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source11/source target11/target encodingUTF-8/encoding /configuration /plugin /plugins /build这里有个小细节source和target既可以写1.8也可以写8Maven 都会转换给javac。但为了减少新人的困惑我建议团队内部统一一种写法。选版本号时也不要头脑发热选最新先确认团队其他成员本地的 JDK 版本是否支持。比如项目里还有人用 JDK 8你直接配release 17他们本地编译必然报错。所以编译版本应该是团队共识不能只是某个人自己机器上的配置。3.3 实用用release替代 source/target如果你的项目用的是 JDK 9 或更高版本我更推荐直接用maven.compiler.release。它和source/target最大的区别是--release会同时限制编译时的 API。什么意思呢就是你在代码里不小心使用了 JDK 17 才有的新 API但配置的目标版本是 11javac会直接报错提醒你“用错了 API”。而如果只用source/targetjavac可能只控制语法和字节码版本不限制 API 引用容易编译通过结果跑到低版本 JRE 上直接抛NoSuchMethodError。配置方式很简单properties maven.compiler.release11/maven.compiler.release /properties或者写在插件里configuration release11/release /configuration需要注意release的版本不能高于你当前实际使用的 JDK 版本。比如你用的是 JDK 8却配置release 11javac根本不认识 11照样报“不支持发行版本 11”。所以release不是万能药它必须在当前 JDK 的支持范围内使用。它的价值是让整个编译过程更规范避免低版本运行环境踩到 API 兼容性的雷。3.4 兜底检查全局 settings.xml 与 Maven Runner还有一种情况比较隐蔽项目pom.xml看起来没问题但还是报错。这时候要想到全局配置文件。Maven 的~/.m2/settings.xml里可以定义 profileprofile 里可以设置maven.compiler.source、maven.compiler.target这些属性。如果全局 settings 里有个激活的 profile 定义了旧版本而项目pom.xml里又没有显式覆盖Maven 合并配置时就会把这个旧版本带进来导致javac不认。解决办法是在项目pom.xml的properties里显式设置覆盖掉全局配置或者去~/.m2/settings.xml把没用的 profile 清理掉。IDEA 里也一样Settings Build Tools Maven Runner JRE那一项要特别留意。如果选的是Use Project JDK就跟随项目 SDK如果手动选了某个版本就可能和命令行环境不一致。我的习惯是把 Runner 的 JRE 明确指到和项目 SDK 同一个 JDK并且记住两边的版本号避免精神分裂式的编译行为。4. 排查实录完整走一遍定位流程4.1 第一步确认当前 Maven 使用的 JDK遇到报错先不要急着改代码第一步先确定 Maven 到底跑在哪个 JDK 上。打开终端执行mvn -version你会看到类似这样的输出Apache Maven 3.9.6 Java version: 17.0.9, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk-17输出里的Java version就是 Maven 当前使用的运行时 JDK。如果你想知道环境变量JAVA_HOME指向哪里再执行echo %JAVA_HOME% # Windows echo $JAVA_HOME # Linux / macOS如果JAVA_HOME和mvn -version显示的不一致多半是环境变量配置的问题。macOS 用户还可以用/usr/libexec/java_home -V列出系统里所有已安装的 JDK检查 PATH 里到底排到了哪一个。这一步很重要因为后续所有判断都是建立在“Maven 用的是哪个 JDK”这个事实上的。4.2 第二步确认项目期望的编译版本第二步是搞清楚项目到底期望用哪个版本编译。打开pom.xml搜索maven.compiler或者maven-compiler-plugin。如果完全找不到相关配置那默认值就是 1.5。如果properties里有maven.compiler.source和maven.compiler.target看这两个值是多少如果插件配置里有source和target也要一并记录。这里要注意一点很多企业项目有父级pom.xml里面可能定义了统一的编译版本。你自己项目里没写不代表没继承。比如公司内部会有一个company-parent里面可能写着maven.compiler.source8子模块默认继承这个值。如果你只看自己的pom.xml很容易漏掉。最好的办法是执行mvn help:effective-pom这条命令会输出合并了父 pom、settings.xml、profile 之后真正生效的完整配置。搜一下maven.compiler.source和maven.compiler.target就能非常准确地知道“期望版本”到底是什么。这一步能省掉很多无头绪的猜测。4.3 第三步对照检查 pom 配置和 IDE 设置当你知道 Maven 实际用的是 JDK 17项目最终生效的编译版本是 1.5问题基本就定性了。接下来要做的是统一版本。如果你不想改pom.xml可以临时把 IDEA 里的 Language Level、Project SDK、Maven Runner JRE 全部改成 17然后重新加载项目。但这种方案只解决你本机的问题。如果你想从根源解决就修改pom.xml把source/target改成项目实际需要的版本或者使用release。改完以后打开 IDEA 的 Maven 工具窗口点击Reload All Maven Projects让 IDE 重新读取配置。然后再看一遍Project Structure Project里的 Language Level确认它已经和pom.xml一致。如果还没同步手动改一下避免 IDEA 继续用旧的语言级别去编译。这个顺序很重要很多同学只改了pom.xml不刷新或者只改 IDE 不落库到 pom结果两边反复横跳问题一直复现。5. 常见问题速查与避坑心得5.1 问题速查表我把实际排查中遇到的高频情况和解决方案整理成了一张表可以直接对照着查现象可能原因推荐方案报“不支持发行版本 5”pom 未配置编译版本Maven 默认 1.5而 JDK 较新在 pom 中配置 source/target 或 release建议 8 以上报“不支持发行版本 8”运行 Maven 的 JDK 太老或者 release 值超过 JDK 版本升级 JDK或把编译版本调到当前 JDK 支持范围IDEA 内刷新正常命令行报错命令行 JAVA_HOME 和 IDEA Runner JRE 不一致统一 JAVA_HOME 和 Runner JREparent pom 传入老版本配置项目继承了父级编译版本旧值覆盖了默认值在子项目 properties 显式覆盖版本号maven-compiler-plugin 太老老插件默认 1.5且与高版本 JDK 兼容差升级插件到 3.8.1 及以上source/target 设置了但没用IDEA 缓存或未 Reload Maven 项目执行 Reload All Maven Projects必要时清缓存重启这张表覆盖了我遇到的九成以上情况。剩下的一成往往是多个原因叠加。比如 IDE 和命令行各跑一套 JDK同时 pom 继承的父版本又写死了一个更老的值。处理这种问题不要只改一处要把编译版本、运行 JDK、IDE 设置三条线全部对齐基本就能根除。5.2 我在实战中踩过的几个细节坑第一改完pom.xml后不要只点 IDEA 里的构建按钮一定要到 Maven 工具窗口点一次Reload All Maven Projects。IDEA 不会自动读取所有 pom 变化有些配置要刷新后才生效。如果不刷新你可能以为改坏了其实只是 IDE 还拿着旧配置。我见过太多同事改了 pom然后直接点运行发现报错依旧就开始往错误方向排查浪费不少时间。第二maven.compiler.source和maven.compiler.target可以写成1.8也可以写成8但同一个项目里混着写很容易让后来的人困惑。我更建议在 JDK 9 的项目里统一用maven.compiler.release写法简单也能避免 source 和 target 不一致的诡异情况。如果团队还在 JDK 8那只能继续用 source/target因为 JDK 8 的javac不支持--release参数。第三有些框架会在编译期生成代码比如 Lombok、QueryDSL、MapStruct它们的注解处理器版本对 JDK 版本特别敏感。当你把 JDK 从 8 升到 17 后Lombok 旧版本可能直接罢工报一堆“找不到符号”或“程序包不存在”。这时候别还以为是编译版本问题检查一下 Lombok 版本是否支持当前 JDK。我自己踩过这个坑Lombok 1.18.20 以上才对 JDK 16 有较好的支持太老的版本必须升级。同理maven-assembly-plugin、maven-javadoc-plugin这类构建插件也可能因为 JDK 升级闹脾气要一并对齐。第四多模块项目要注意每个模块的编译配置。很多项目只在根 pom 配置了 properties子模块如果没有显式声明默认会继承父级。但如果你在某个子模块里重新定义了 properties就可能覆盖全局配置。排查时建议用mvn -pl 子模块 help:effective-pom看实际生效的配置别凭感觉猜。我曾经在一个子模块里看到编译版本还是 1.5而根 pom 已经写了 11最后发现是那个子模块的 pom 里有一条多余的properties把值覆盖回去了删掉后一切正常。最后再分享一个小技巧遇到这种编译报错与其反复调版本不如先统一团队约定。可以在项目 README 里写明“本工程统一使用 JDK 11编译版本 release 11”并且在根 pom 的 properties 里写死配合.sdkmanrc或.java-version文件让成员切换 JDK 时有一个统一参考。团队里少一点“我这儿编译好好的”的争论后面能少踩很多坑。我自己后来把所有新项目的 pom 模板都固定成 release 版本配置连 IDEA 里的新项目 SDK 也统一之后再没被“不支持发行版本”拦过路。