Kotlin Maven插件疑似忽略jvmTarget配置,是否属于插件Bug?
环境信息
- Spring Boot: 3.2.0
- Kotlin: 1.9.21
- 构建镜像: maven:3.9.5-amazoncorretto-21
- 运行镜像: amazoncorretto:17
我有一个简单的Spring Boot项目,核心代码如下:
@SpringBootApplication class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) listOf(1,2,3,4).removeLast() }
项目的pom.xml配置:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> <kotlin.version>1.9.21</kotlin.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory> <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <configuration> <args> <arg>-Xjsr305=strict</arg> </args> <compilerPlugins> <plugin>spring</plugin> </compilerPlugins> <jvmTarget>${java.version}</jvmTarget> </configuration> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-allopen</artifactId> <version>${kotlin.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
注意Kotlin插件中配置了<jvmTarget>${java.version}</jvmTarget>,指向Java 17。
Dockerfile内容:
FROM maven:3.9.5-amazoncorretto-21 AS builder RUN mkdir "/app" WORKDIR /app COPY src ./src COPY pom.xml . RUN mvn clean package FROM amazoncorretto:17 COPY --from=builder /app/target/demo-0.0.1-SNAPSHOT.jar ./app.jar ENTRYPOINT ["java", "-jar", "app.jar"]
执行以下命令构建并运行:
docker build -t demo .
docker run demo
运行时抛出异常:
INFO 1 --- [ main] com.example.demo.DemoApplicationKt : Starting DemoApplicationKt v0.0.1-SNAPSHOT using Java 17.0.9 with PID 1 (/app.jar started by root in /) Exception in thread "main" java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) Caused by: java.lang.NoSuchMethodError: 'java.lang.Object java.util.List.removeLast()' at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:12)
查阅removeLast()方法文档可知,该方法是Java 21新增的:
Removes and returns the last element of this collection (optional operation).
Throws:
NoSuchElementException – if this collection is empty
UnsupportedOperationException – if this collection implementation does not support this operation
Implementation Requirements:
If this List is not empty, the implementation in this interface returns the result of calling remove(size() - 1). Otherwise, it throws NoSuchElementException.
Since:21
显然Kotlin插件似乎忽略了<jvmTarget>配置,把构建镜像换成maven:3.9.5-amazoncorretto-17后问题解决。
请问这是否是Kotlin插件的Bug?
这不是Kotlin插件的Bug,而是构建环境JDK版本高于目标JVM版本时的正常编译行为。
原因解析
jvmTarget参数仅控制生成的字节码版本(比如class文件的版本号),但不会自动替换对高版本JDK API的调用。当用JDK 21编译时,Kotlin编译器会直接使用JDK 21中新增的List.removeLast()方法,而不会生成兼容JDK 17的替代逻辑——即使你指定了jvmTarget=17。
解决方案
有两种方式可以解决这个问题:
- 保持构建与运行环境JDK版本一致:改用JDK 17的构建镜像,此时编译器会检测到目标环境不支持
removeLast(),自动使用Kotlin标准库中的兼容实现(比如调用remove(size-1))。 - 在Kotlin插件中指定
jdkHome:强制编译器基于JDK 17的API进行编译,即使构建环境是JDK 21。示例配置如下:
<plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <configuration> <args> <arg>-Xjsr305=strict</arg> </args> <compilerPlugins> <plugin>spring</plugin> </compilerPlugins> <jvmTarget>${java.version}</jvmTarget> <jdkHome>/path/to/jdk17</jdkHome> <!-- 指定JDK17的路径 --> </configuration> <!-- 其他依赖配置 --> </plugin>
总结来说,jvmTarget不负责API兼容性检查,它只控制字节码版本。要确保编译出的代码能在低版本JDK运行,要么用对应版本的JDK编译,要么明确指定jdkHome让编译器基于目标JDK的API进行编译。
内容的提问来源于stack exchange,提问作者Vahid

