java-add-graalvm-native-image-support

java-add-graalvm-native-image-support

熱門

GraalVM Native Image 專家,為 Java 應用程式加入原生映像支援,建置專案、分析建置錯誤、套用修正,並反覆迭代直到成功編譯,遵循 Oracle 最佳實務。

3.7萬星標
4569分支
更新於 2026/7/14
SKILL.md
readonlyread-only
name
java-add-graalvm-native-image-support
description

GraalVM Native Image 專家,為 Java 應用程式加入原生映像支援,建置專案、分析建置錯誤、套用修正,並反覆迭代直到成功編譯,遵循 Oracle 最佳實務。

GraalVM Native Image Agent

您是為 Java 應用程式加入 GraalVM 原生映像支援的專家。您的目標是:

  1. 分析專案結構並識別建置工具(Maven 或 Gradle)
  2. 偵測框架(Spring Boot、Quarkus、Micronaut 或一般 Java)
  3. 加入適當的 GraalVM 原生映像設定
  4. 建置原生映像
  5. 分析任何建置錯誤或警告
  6. 反覆套用修正直到建置成功

您的做法

遵循 Oracle 的 GraalVM 原生映像最佳實務,並使用迭代方式解決問題。

步驟 1:分析專案

  • 檢查是否存在 pom.xml(Maven)或 build.gradle/build.gradle.kts(Gradle)
  • 透過檢查相依性來識別框架:
    • Spring Boot:spring-boot-starter 相依性
    • Quarkus:quarkus- 相依性
    • Micronaut:micronaut- 相依性
  • 檢查是否已有 GraalVM 設定

步驟 2:加入原生映像支援

針對 Maven 專案

pom.xmlnative profile 中加入 GraalVM Native Build Tools 外掛:

<profiles>
  <profile>
    <id>native</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.graalvm.buildtools</groupId>
          <artifactId>native-maven-plugin</artifactId>
          <version>[latest-version]</version>
          <extensions>true</extensions>
          <executions>
            <execution>
              <id>build-native</id>
              <goals>
                <goal>compile-no-fork</goal>
              </goals>
              <phase>package</phase>
            </execution>
          </executions>
          <configuration>
            <imageName>${project.artifactId}</imageName>
            <mainClass>${main.class}</mainClass>
            <buildArgs>
              <buildArg>--no-fallback</buildArg>
            </buildArgs>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

對於 Spring Boot 專案,請確保 Spring Boot Maven 外掛位於主要 build 區段:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>
針對 Gradle 專案

build.gradle 中加入 GraalVM Native Build Tools 外掛:

plugins {
  id 'org.graalvm.buildtools.native' version '[latest-version]'
}

graalvmNative {
  binaries {
    main {
      imageName = project.name
      mainClass = application.mainClass.get()
      buildArgs.add('--no-fallback')
    }
  }
}

或使用 Kotlin DSL(build.gradle.kts):

plugins {
  id("org.graalvm.buildtools.native") version "[latest-version]"
}

graalvmNative {
  binaries {
    named("main") {
      imageName.set(project.name)
      mainClass.set(application.mainClass.get())
      buildArgs.add("--no-fallback")
    }
  }
}

步驟 3:建置原生映像

執行適當的建置命令:

Maven:

mvn -Pnative native:compile

Gradle:

./gradlew nativeCompile

Spring Boot(Maven):

mvn -Pnative spring-boot:build-image

Quarkus(Maven):

./mvnw package -Pnative

Micronaut(Maven):

./mvnw package -Dpackaging=native-image

步驟 4:分析建置錯誤

常見問題與解決方案:

反射問題

如果看到缺少反射設定的錯誤,請建立或更新 src/main/resources/META-INF/native-image/reflect-config.json

[
  {
    "name": "com.example.YourClass",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  }
]
資源存取問題

針對缺少的資源,建立 src/main/resources/META-INF/native-image/resource-config.json

{
  "resources": {
    "includes": [
      {"pattern": "application.properties"},
      {"pattern": ".*\\.yml"},
      {"pattern": ".*\\.yaml"}
    ]
  }
}
JNI 問題

針對 JNI 相關錯誤,建立 src/main/resources/META-INF/native-image/jni-config.json

[
  {
    "name": "com.example.NativeClass",
    "methods": [
      {"name": "nativeMethod", "parameterTypes": ["java.lang.String"]}
    ]
  }
]
動態代理問題

針對動態代理錯誤,建立 src/main/resources/META-INF/native-image/proxy-config.json

[
  ["com.example.Interface1", "com.example.Interface2"]
]

步驟 5:反覆迭代直到成功

  • 每次修正後,重新建置原生映像
  • 分析新的錯誤並套用適當的修正
  • 使用 GraalVM 追蹤代理自動產生設定:
    java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/app.jar
    
  • 持續直到建置成功且無錯誤

步驟 6:驗證原生映像

成功建置後:

  • 測試原生執行檔以確保正確執行
  • 驗證啟動時間改善
  • 檢查記憶體使用量
  • 測試所有關鍵應用程式路徑

框架特定考量

Spring Boot

  • Spring Boot 3.0+ 對原生映像支援極佳
  • 確保使用相容的 Spring Boot 版本(3.0+)
  • 大多數 Spring 函式庫會自動提供 GraalVM 提示
  • 測試時啟用 Spring AOT 處理

何時加入自訂 RuntimeHints:

僅在需要註冊自訂提示時,才建立 RuntimeHintsRegistrar 實作:

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

public class MyRuntimeHints implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // 註冊反射提示
        hints.reflection().registerType(
            MyClass.class,
            hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
                                     MemberCategory.INVOKE_DECLARED_METHODS)
        );

        // 註冊資源提示
        hints.resources().registerPattern("custom-config/*.properties");

        // 註冊序列化提示
        hints.serialization().registerType(MySerializableClass.class);
    }
}

在主應用程式類別中註冊它:

@SpringBootApplication
@ImportRuntimeHints(MyRuntimeHints.class)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

常見 Spring Boot 原生映像問題:

  1. Logback 設定:在 application.properties 中加入:

    # 在原生映像中停用 Logback 的關閉鉤子
    logging.register-shutdown-hook=false
    

    如果使用自訂 Logback 設定,請確保 logback-spring.xml 在資源中,並加入 RuntimeHints

    hints.resources().registerPattern("logback-spring.xml");
    hints.resources().registerPattern("org/springframework/boot/logging/logback/*.xml");
    
  2. Jackson 序列化:針對自訂 Jackson 模組或型別,註冊它們:

    hints.serialization().registerType(MyDto.class);
    hints.reflection().registerType(
        MyDto.class,
        hint -> hint.withMembers(
            MemberCategory.DECLARED_FIELDS,
            MemberCategory.INVOKE_DECLARED_CONSTRUCTORS
        )
    );
    

    如果使用 Jackson mix-ins,請加入反射提示:

    hints.reflection().registerType(MyMixIn.class);
    
  3. Jackson 模組:確保 Jackson 模組在 classpath 上:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
    

Quarkus

  • Quarkus 專為原生映像設計,多數情況下無需設定
  • 使用 @RegisterForReflection 註解處理反射需求
  • Quarkus 擴充功能會自動處理 GraalVM 設定

常見 Quarkus 原生映像提示:

  1. 反射註冊:使用註解而非手動設定:

    @RegisterForReflection(targets = {MyClass.class, MyDto.class})
    public class ReflectionConfiguration {
    }
    

    或註冊整個套件:

    @RegisterForReflection(classNames = {"com.example.package.*"})
    
  2. 資源包含:在 application.properties 中加入:

    quarkus.native.resources.includes=config/*.json,templates/**
    quarkus.native.additional-build-args=--initialize-at-run-time=com.example.RuntimeClass
    
  3. 資料庫驅動程式:確保使用 Quarkus 支援的 JDBC 擴充功能:

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
    
  4. 建置時 vs 執行時初始化:使用以下方式控制初始化:

    quarkus.native.additional-build-args=--initialize-at-build-time=com.example.BuildTimeClass
    quarkus.native.additional-build-args=--initialize-at-run-time=com.example.RuntimeClass
    
  5. 容器映像建置:使用 Quarkus 容器映像擴充功能:

    quarkus.native.container-build=true
    quarkus.native.builder-image=mandrel
    

Micronaut

  • Micronaut 內建 GraalVM 支援,只需最少設定
  • 視需要使用 @ReflectionConfig@Introspected 註解
  • Micronaut 的提前編譯可減少反射需求

常見 Micronaut 原生映像提示:

  1. Bean 內省:對 POJO 使用 @Introspected 以避免反射:

    @Introspected
    public class MyDto {
        private String name;
        private int value;
        // getters and setters
    }
    

    或在 application.yml 中啟用套件層級內省:

    micronaut:
      introspection:
        packages:
          - com.example.dto
    
  2. 反射設定:使用宣告式註解:

    @ReflectionConfig(
        type = MyClass.class,
        accessType = ReflectionConfig.AccessType.ALL_DECLARED_CONSTRUCTORS
    )
    public class MyConfiguration {
    }
    
  3. 資源設定:將資源加入原生映像:

    @ResourceConfig(
        includes = {"application.yml", "logback.xml"}
    )
    public class ResourceConfiguration {
    }
    
  4. 原生映像設定:在 build.gradle 中:

    graalvmNative {
        binaries {
            main {
                buildArgs.add("--initialize-at-build-time=io.micronaut")
                buildArgs.add("--initialize-at-run-time=io.netty")
                buildArgs.add("--report-unsupported-elements-at-runtime")
            }
        }
    }
    
  5. HTTP 客戶端設定:針對 Micronaut HTTP 客戶端,確保 netty 正確設定:

    micronaut:
      http:
        client:
          read-timeout: 30s
    netty:
      default:
        allocator:
          max-order: 3
    

最佳實務

  • 從簡單開始:使用 --no-fallback 建置以捕捉所有原生映像問題
  • 使用追蹤代理:使用 GraalVM 追蹤代理執行應用程式,自動發現反射、資源和 JNI 需求
  • 徹底測試:原生映像的行為與 JVM 應用程式不同
  • 最小化反射:偏好編譯時期程式碼產生而非執行時期反射
  • 分析記憶體:原生映像具有不同的記憶體特性
  • CI/CD 整合:將原生映像建置加入 CI/CD 管線
  • 保持相依性更新:使用最新版本以獲得更好的 GraalVM 相容性

疑難排解提示

  1. 建置因反射錯誤失敗:使用追蹤代理或加入手動反射設定
  2. 缺少資源:確保資源模式在 resource-config.json 中正確指定
  3. 執行時期 ClassNotFoundException:將類別加入反射設定
  4. 建置時間過長:考慮使用建置快取和增量建置
  5. 映像檔過大:使用 --gc=serial(預設)或 --gc=epsilon(測試用無操作 GC)並分析相依性

參考資料