如何使用Maven将Java 17代码编译为Java 8及更早兼容版本
你使用JDK 17作为编译执行环境时,javac原生不支持将语法兼容版本(source)设为17的同时,生成Java 8版本的目标字节码(target)。从JDK 9引入模块化机制后,javac对source版本高于target版本的跨版本编译场景做了跨度限制,这是你触发报错的直接原因。
分两种场景选择适配方案:
场景1:不需要使用Java 17的新语法,仅本地开发环境为JDK 17,需要输出Java 8兼容产物
直接调整Maven编译配置即可,同时增加参数避免误用到高版本核心类库:
<properties> <!-- 限制代码语法仅能使用Java 8及以下的特性 --> <maven.compiler.source>1.8</maven.compiler.source> <!-- 生成的字节码兼容Java 8及以上版本 --> <maven.compiler.target>1.8</maven.compiler.target> <!-- 编译时关联Java 8的核心类库,避免编译期通过但运行在Java 8时出现类/方法不存在的错误 --> <maven.compiler.release>8</maven.compiler.release> </properties>
你之前单独使用release参数未解决问题的原因是:release是JDK 9+新增的参数,会同时锁定source、target、核心类库三个版本,如果你指定release=17则自动锁定target为17,自然无法得到Java 8兼容的产物。
场景2:需要使用Java 17的语法特性(如var、文本块、record、instanceof模式匹配等),同时要求产物可在Java 8上运行
原生javac不支持该场景,需要使用第三方编译插件Jabel实现语法转译,配套API兼容性校验的配置如下:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>1.8</target> <release>8</release> <compilerArgs> <arg>--enable-preview</arg> <arg>-Xlint:-preview</arg> </compilerArgs> <annotationProcessorPaths> <path> <groupId>com.github.bsideup.jabel</groupId> <artifactId>jabel-javac-plugin</artifactId> <version>0.4.2</version> </path> </annotationProcessorPaths> </configuration> </plugin> <!-- 校验代码是否用到Java 8以上的核心类库API,避免运行时报错 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.23</version> <executions> <execution> <id>check-java8-compatibility</id> <phase>verify</phase> <goals> <goal>check</goal> </goals> <configuration> <signature> <groupId>org.codehaus.mojo.signature</groupId> <artifactId>java18</artifactId> <version>1.0</version> </signature> </configuration> </execution> </executions> </plugin> </plugins> </build>
注意:该方案仅支持语法特性的向下兼容,Java 8之后新增的核心类库API(如HttpClient、Stream新增方法等)无法被转译,运行在Java 8环境时会抛出NoClassDefFoundError/NoSuchMethodError异常,所以需要配套animal-sniffer插件做编译期校验。
两个参数的作用完全独立,不存在必须一致的设计要求:
source:限制代码可使用的语法特性版本,超过该版本的语法会在编译期直接报错,和生成的字节码版本无关target:指定最终生成的.class字节码的版本,决定了可运行该产物的最低JRE版本,和代码使用的语法版本无强制绑定
低版本JDK作为编译环境时可以自由跨版本配置,比如用JDK 8编译时,可配置source=1.8、target=1.6,生成能运行在Java 6环境的字节码。仅高版本JDK(9+)对source高于target的跨度做了限制,才会出现你遇到的版本兼容报错,和参数本身的设计无关。你使用的Maven 3.8.3版本完全兼容上述所有配置,无需调整Maven版本。
内容的提问来源于stack exchange,提问作者Mansur

