You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Maven插件疑似忽略jvmTarget配置,是否属于插件Bug?

问题:Kotlin编译时忽略jvmTarget配置导致运行报错?

环境信息

  • 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。

解决方案

有两种方式可以解决这个问题:

  1. 保持构建与运行环境JDK版本一致:改用JDK 17的构建镜像,此时编译器会检测到目标环境不支持removeLast(),自动使用Kotlin标准库中的兼容实现(比如调用remove(size-1))。
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 22:37:09