GraalVM原生镜像部署Spring Boot应用时出现依赖注入异常
Spring Boot原生镜像部署K8S无法注册Eureka问题
我用Spring 3.1.2开发了一个简单的REST应用,打包成JAR部署到K8S集群时能正常注册到Eureka服务器并运行。但用mvn clean -Pnative native:compile编译成GraalVM原生镜像后部署,日志出现如下错误:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.1.2) 2023-10-09T22:33:25.141-05:00 INFO 30700 --- [ main] c.e.creditscore.CreditscoreApplication : Starting AOT-processed CreditscoreApplication using Java 17.0.8 with PID 30700 (/Users/atael/Documents/GitHub/microservices-datadriven/cloudbank-v2/spring-apps-spring3/creditscore/target/creditscore started by atael in /Users/atael/Documents/GitHub/microservices-datadriven/cloudbank-v2/spring-apps-spring3/creditscore/target) 2023-10-09T22:33:25.142-05:00 INFO 30700 --- [ main] c.e.creditscore.CreditscoreApplication : No active profile set, falling back to 1 default profile: "default" 2023-10-09T22:33:25.144-05:00 INFO 30700 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=368d3e47-1089-3f9a-85bc-0c3f5dcda5ab 2023-10-09T22:33:25.162-05:00 INFO 30700 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-10-09T22:33:25.163-05:00 INFO 30700 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-10-09T22:33:25.163-05:00 INFO 30700 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.11] 2023-10-09T22:33:25.170-05:00 INFO 30700 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-10-09T22:33:25.170-05:00 INFO 30700 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 28 ms 2023-10-09T22:33:25.195-05:00 WARN 30700 --- [ main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'healthContributorRegistry': Unsatisfied dependency expressed through method 'healthContributorRegistry' parameter 2: Error creating bean with name 'discoveryCompositeHealthContributor': Unsatisfied dependency expressed through method 'discoveryCompositeHealthContributor' parameter 0: Error creating bean with name 'eurekaHealthIndicator': Unsatisfied dependency expressed through method 'eurekaHealthIndicator' parameter 0: No qualifying bean of type 'com.netflix.discovery.EurekaClient' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
pom.xml相关配置
<plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin>
使用的Java版本
java version "17.0.8" 2023-07-18 LTS Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14) Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
应用代码
启动类
package com.example.creditscore; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class CreditscoreApplication { public static void main(String[] args) { SpringApplication.run(CreditscoreApplication.class, args); } }
控制器
package com.example.creditscore.controller; import org.springframework.web.bind.annotation.*; import lombok.extern.slf4j.Slf4j; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/v1") @Slf4j public class CreditScoreController { @GetMapping("/creditscore") public Map<String, String> getCreditScore() { log.info("CREDITSCORE: getCreditScore"); int max = 900; int min = 500; SecureRandom secureRandom = new SecureRandom(); HashMap<String, String> map = new HashMap<>(); map.put("Credit Score", String.valueOf(secureRandom.nextInt(max - min) + min)); map.put("Date", String.valueOf(java.time.LocalDate.now())); return map; } }
application.yaml配置
spring: application: name: creditscore zipkin: base-url: ${zipkin.base-url} eureka: instance: hostname: ${spring.application.name} preferIpAddress: true client: service-url: defaultZone: ${eureka.service-url} fetch-registry: true register-with-eureka: true enabled: true management: endpoint: health: show-details: always endpoints: web: exposure: include: "*" metrics: tags: application: ${spring.application.name}
K8S Deployment描述信息
Name: creditscore Namespace: nativetest CreationTimestamp: Mon, 09 Oct 2023 15:53:20 -0500 Labels: <none> Annotations: deployment.kubernetes.io/revision: 1 Selector: app=creditscore Replicas: 1 desired | 1 updated | 1 total | 0 available | 1 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=creditscore version=0.0.1 Containers: creditscore: Image: <notforpublic> Port: 8080/TCP Host Port: 0/TCP Environment: app.container.port: 8080 spring.profiles.active: default spring.config.label: 0.0.1 eureka.instance.preferIpAddress: true eureka.instance.hostname: creditscore.nativetest MP_LRA_COORDINATOR_URL: http://otmm-tcs.otmm.svc.cluster.local:9000/api/v1/lra-coordinator MP_LRA_PARTICIPANT_URL: http://creditscore.nativetest.svc.cluster.local:8080 eureka.client.register-with-eureka: true eureka.client.fetch-registry: true eureka.client.service-url.defaultZone: http://eureka.eureka:8761/eureka zipkin.base-url: http://jaegertracing-collector.observability:9411 otel.exporter.otlp.endpoint: http://open-telemetry-opentelemetry-collector.open-telemetry:4317 hystrix.metrics.enabled: true Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Progressing True NewReplicaSetAvailable Available False MinimumReplicasUnavailable OldReplicaSets: <none> NewReplicaSet: creditscore-5774cb4757 (1/1 replicas created) Events: <none>
请问我哪里操作出错了?
解决方法
问题核心是GraalVM原生镜像的AOT处理未正确生成Eureka客户端相关Bean,导致Spring上下文无法找到com.netflix.discovery.EurekaClient类型实例。可按以下步骤修复:
- 补充Spring Cloud Native依赖
确保pom.xml中加入适配原生镜像的Spring Cloud组件依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-native-core</artifactId> </dependency>
- 完善原生镜像编译插件配置
增强native-maven-plugin的配置,确保Spring Boot AOT处理被正确触发:
<plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <extensions>true</extensions> <executions> <execution> <id>build-native</id> <goals> <goal>compile-no-fork</goal> </goals> <phase>package</phase> </execution> </executions> </plugin>
- 手动指定AOT注册类(可选)
如果自动处理仍有遗漏,可在启动类添加@NativeHint指定需要预注册的Eureka相关类:
@EnableDiscoveryClient @SpringBootApplication @NativeHint(types = {com.netflix.discovery.EurekaClient.class, org.springframework.cloud.client.discovery.DiscoveryClient.class}) public class CreditscoreApplication { public static void main(String[] args) { SpringApplication.run(CreditscoreApplication.class, args); } }
- 验证版本兼容性
确保使用的Spring Cloud版本(2022.0.x系列)与Spring Boot 3.1.x完全匹配,避免版本冲突导致的原生镜像适配问题。
内容的提问来源于stack exchange,提问作者Tallen67
相关产品推荐
相关产品推荐

