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

Spring Boot+GraalVM构建Native Image遇动态代理UnsupportedFeatureError

问题描述

我正尝试用Gradle插件将基于Kotlin+JPA+Hibernate的Spring Boot应用编译为GraalVM Native Image,编译完成后运行生成的可执行文件时出现如下错误:

com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface org.hibernate.query.hql.spi.SqmQueryImplementor, interface org.hibernate.query.sqm.internal.SqmInterpretationsKey$InterpretationsKeySource, interface org.hibernate.query.spi.DomainQueryExecutionContext, interface org.hibernate.query.SelectionQuery, interface org.hibernate.query.CommonQueryContract] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
    at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:92) ~[na:na]
    at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:171) ~[na:na]
    at java.base@20.0.1/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[PriceMonitoringBackend:na]
    at java.base@20.0.1/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1034) ~[PriceMonitoringBackend:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:316) ~[na:na]
    at jdk.proxy4/jdk.proxy4.$Proxy73.createQuery(Unknown Source) ~[na:na]
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:297) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:242) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery(PartTreeJpaQuery.java:113) ~[na:na]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:234) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:223) ~[na:na]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:92) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:148) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:136) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[na:na]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:72) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[na:na]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94) ~[na:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[PriceMonitoringBackend:6.0.11]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244) ~[na:na]
    at jdk.proxy4/jdk.proxy4.$Proxy58.findByEmail(Unknown Source) ~[na:na]
    at iee.ihu.gr.pricemonitoringbackend.DevInitializer.run(PriceApplicationInitializers.kt:51) ~[PriceMonitoringBackend:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:319) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[PriceMonitoringBackend:3.1.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[PriceMonitoringBackend:3.1.2]
    at iee.ihu.gr.pricemonitoringbackend.PriceMonitoringBackendApplicationKt.main(PriceMonitoringBackendApplication.kt:17) ~[PriceMonitoringBackend:na]

错误出现在用于向数据库插入测试数据的应用初始化器中,由以下代码触发:

val testUser = customerRepository.findByEmail("test@email.com").get()

该代码调用的Spring JPA仓库定义如下:

interface CustomerRepository : JpaRepository<Customer,Long>{
    fun findByEmail(email: String) : Optional<Customer>
}

我的Gradle构建文件配置如下:

plugins {
    id("org.springframework.boot") version "3.1.2"
    id("io.spring.dependency-management") version "1.1.2"
    id("org.graalvm.buildtools.native") version "0.9.23"
    kotlin("jvm") version "1.8.22"
    kotlin("plugin.spring") version "1.8.22"
    kotlin("plugin.jpa") version "1.8.22"
    kotlin("plugin.serialization") version "1.8.22"
}

group = "x.y.z"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

extra["testcontainersVersion"] = "1.17.6"

dependencies {
    //spring
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-hateoas")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.security:spring-security-oauth2-resource-server")
    implementation("org.springframework.security:spring-security-oauth2-jose")
    implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
    implementation("org.springframework.boot:spring-boot-starter-logging")
    implementation("org.springframework.boot:spring-boot-starter-amqp")


    //kotlin
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")

    //other
    implementation("com.mysql:mysql-connector-j")
    implementation("org.hibernate.validator:hibernate-validator")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.0")

    //test
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:mysql")
    testImplementation("io.kotest:kotest-assertions-core-jvm:5.5.5")
    implementation("com.ninja-squad:springmockk:3.1.2")

}

dependencyManagement {
    imports {
        mavenBom("org.testcontainers:testcontainers-bom:${property("testcontainersVersion")}")
    }
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}
解决方案

该错误源于GraalVM Native Image不支持运行时动态生成代理类,必须在镜像构建阶段提前声明需要生成的代理接口组合。

步骤1:创建动态代理配置文件

在项目的src/main/resources目录下创建proxy-config.json文件,内容如下:

[
  {
    "interfaces": [
      "org.hibernate.query.hql.spi.SqmQueryImplementor",
      "org.hibernate.query.sqm.internal.SqmInterpretationsKey$InterpretationsKeySource",
      "org.hibernate.query.spi.DomainQueryExecutionContext",
      "org.hibernate.query.SelectionQuery",
      "org.hibernate.query.CommonQueryContract"
    ]
  }
]

步骤2:修改Gradle配置,添加Native Image构建参数

在Gradle构建文件中,配置GraalVM Native插件的构建参数,指定代理配置文件:

graalvmNative {
    binaries {
        main {
            buildArgs.add("-H:DynamicProxyConfigurationResources=proxy-config.json")
        }
    }
}

补充说明

  • 若后续仍出现其他代理类找不到的错误,只需将对应接口组合添加到proxy-config.json中即可。
  • 也可通过native-image-agent自动生成代理配置文件,覆盖所有需要的代理接口:
    1. 以JVM模式运行应用:./gradlew bootRun
    2. 执行所有触发代理生成的操作(如调用仓库方法、执行初始化逻辑等)
    3. 停止应用,代理配置文件会生成在build/native-agent-output目录下
    4. 将生成的配置文件复制到src/main/resources,并在Gradle中指定使用

内容的提问来源于stack exchange,提问作者Barracuda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 15:54:52