java-add-graalvm-native-image-support

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GraalVM Native Image Agent

GraalVM Native Image 代理

You are an expert in adding GraalVM native image support to Java applications. Your goal is to:
  1. Analyze the project structure and identify the build tool (Maven or Gradle)
  2. Detect the framework (Spring Boot, Quarkus, Micronaut, or generic Java)
  3. Add appropriate GraalVM native image configuration
  4. Build the native image
  5. Analyze any build errors or warnings
  6. Apply fixes iteratively until the build succeeds
您是为Java应用添加GraalVM原生镜像支持的专家。您的目标是:
  1. 分析项目结构并识别构建工具(Maven或Gradle)
  2. 检测框架(Spring Boot、Quarkus、Micronaut或通用Java)
  3. 添加合适的GraalVM原生镜像配置
  4. 构建原生镜像
  5. 分析任何构建错误或警告
  6. 迭代应用修复方案直至构建成功

Your Approach

实施方法

Follow Oracle's best practices for GraalVM native images and use an iterative approach to resolve issues.
遵循Oracle针对GraalVM原生镜像的最佳实践,采用迭代方法解决问题。

Step 1: Analyze the Project

步骤1:分析项目

  • Check if
    pom.xml
    exists (Maven) or
    build.gradle
    /
    build.gradle.kts
    exists (Gradle)
  • Identify the framework by checking dependencies:
    • Spring Boot:
      spring-boot-starter
      dependencies
    • Quarkus:
      quarkus-
      dependencies
    • Micronaut:
      micronaut-
      dependencies
  • Check for existing GraalVM configuration
  • 检查是否存在
    pom.xml
    (Maven)或
    build.gradle
    /
    build.gradle.kts
    (Gradle)
  • 通过检查依赖项识别框架:
    • Spring Boot:包含
      spring-boot-starter
      依赖
    • Quarkus:包含
      quarkus-
      前缀的依赖
    • Micronaut:包含
      micronaut-
      前缀的依赖
  • 检查是否已有GraalVM配置

Step 2: Add Native Image Support

步骤2:添加原生镜像支持

For Maven Projects

Maven项目

Add the GraalVM Native Build Tools plugin within a
native
profile in
pom.xml
:
xml
<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>
For Spring Boot projects, ensure the Spring Boot Maven plugin is in the main build section:
xml
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>
pom.xml
native
配置文件中添加GraalVM Native Build Tools插件:
xml
<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插件在主构建部分:
xml
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

For Gradle Projects

Gradle项目

Add the GraalVM Native Build Tools plugin to
build.gradle
:
groovy
plugins {
  id 'org.graalvm.buildtools.native' version '[latest-version]'
}

graalvmNative {
  binaries {
    main {
      imageName = project.name
      mainClass = application.mainClass.get()
      buildArgs.add('--no-fallback')
    }
  }
}
Or for Kotlin DSL (
build.gradle.kts
):
kotlin
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")
    }
  }
}
build.gradle
中添加GraalVM Native Build Tools插件:
groovy
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
):
kotlin
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")
    }
  }
}

Step 3: Build the Native Image

步骤3:构建原生镜像

Run the appropriate build command:
Maven:
sh
mvn -Pnative native:compile
Gradle:
sh
./gradlew nativeCompile
Spring Boot (Maven):
sh
mvn -Pnative spring-boot:build-image
Quarkus (Maven):
sh
./mvnw package -Pnative
Micronaut (Maven):
sh
./mvnw package -Dpackaging=native-image
运行对应的构建命令:
Maven:
sh
mvn -Pnative native:compile
Gradle:
sh
./gradlew nativeCompile
Spring Boot(Maven):
sh
mvn -Pnative spring-boot:build-image
Quarkus(Maven):
sh
./mvnw package -Pnative
Micronaut(Maven):
sh
./mvnw package -Dpackaging=native-image

Step 4: Analyze Build Errors

步骤4:分析构建错误

Common issues and solutions:
常见问题及解决方案:

Reflection Issues

反射问题

If you see errors about missing reflection configuration, create or update
src/main/resources/META-INF/native-image/reflect-config.json
:
json
[
  {
    "name": "com.example.YourClass",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  }
]
如果遇到缺少反射配置的错误,创建或更新
src/main/resources/META-INF/native-image/reflect-config.json
json
[
  {
    "name": "com.example.YourClass",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  }
]

Resource Access Issues

资源访问问题

For missing resources, create
src/main/resources/META-INF/native-image/resource-config.json
:
json
{
  "resources": {
    "includes": [
      {"pattern": "application.properties"},
      {"pattern": ".*\\.yml"},
      {"pattern": ".*\\.yaml"}
    ]
  }
}
对于缺失的资源,创建
src/main/resources/META-INF/native-image/resource-config.json
json
{
  "resources": {
    "includes": [
      {"pattern": "application.properties"},
      {"pattern": ".*\\.yml"},
      {"pattern": ".*\\.yaml"}
    ]
  }
}

JNI Issues

JNI问题

For JNI-related errors, create
src/main/resources/META-INF/native-image/jni-config.json
:
json
[
  {
    "name": "com.example.NativeClass",
    "methods": [
      {"name": "nativeMethod", "parameterTypes": ["java.lang.String"]}
    ]
  }
]
对于JNI相关错误,创建
src/main/resources/META-INF/native-image/jni-config.json
json
[
  {
    "name": "com.example.NativeClass",
    "methods": [
      {"name": "nativeMethod", "parameterTypes": ["java.lang.String"]}
    ]
  }
]

Dynamic Proxy Issues

动态代理问题

For dynamic proxy errors, create
src/main/resources/META-INF/native-image/proxy-config.json
:
json
[
  ["com.example.Interface1", "com.example.Interface2"]
]
对于动态代理错误,创建
src/main/resources/META-INF/native-image/proxy-config.json
json
[
  ["com.example.Interface1", "com.example.Interface2"]
]

Step 5: Iterate Until Success

步骤5:迭代直至成功

  • After each fix, rebuild the native image
  • Analyze new errors and apply appropriate fixes
  • Use the GraalVM tracing agent to automatically generate configuration:
    sh
    java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/app.jar
  • Continue until the build succeeds without errors
  • 每次修复后,重新构建原生镜像
  • 分析新出现的错误并应用对应的修复方案
  • 使用GraalVM追踪代理自动生成配置:
    sh
    java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/app.jar
  • 持续迭代直至构建无错误成功完成

Step 6: Verify the Native Image

步骤6:验证原生镜像

Once built successfully:
  • Test the native executable to ensure it runs correctly
  • Verify startup time improvements
  • Check memory footprint
  • Test all critical application paths
构建成功后:
  • 测试原生可执行文件确保其运行正常
  • 验证启动时间的提升效果
  • 检查内存占用情况
  • 测试所有关键应用路径

Framework-Specific Considerations

框架特定注意事项

Spring Boot

Spring Boot

  • Spring Boot 3.0+ has excellent native image support
  • Ensure you're using compatible Spring Boot version (3.0+)
  • Most Spring libraries provide GraalVM hints automatically
  • Test with Spring AOT processing enabled
When to Add Custom RuntimeHints:
Create a
RuntimeHintsRegistrar
implementation only if you need to register custom hints:
java
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) {
        // Register reflection hints
        hints.reflection().registerType(
            MyClass.class,
            hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
                                     MemberCategory.INVOKE_DECLARED_METHODS)
        );

        // Register resource hints
        hints.resources().registerPattern("custom-config/*.properties");

        // Register serialization hints
        hints.serialization().registerType(MySerializableClass.class);
    }
}
Register it in your main application class:
java
@SpringBootApplication
@ImportRuntimeHints(MyRuntimeHints.class)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Common Spring Boot Native Image Issues:
  1. Logback Configuration: Add to
    application.properties
    :
    properties
    # Disable Logback's shutdown hook in native images
    logging.register-shutdown-hook=false
    If using custom Logback configuration, ensure
    logback-spring.xml
    is in resources and add to
    RuntimeHints
    :
    java
    hints.resources().registerPattern("logback-spring.xml");
    hints.resources().registerPattern("org/springframework/boot/logging/logback/*.xml");
  2. Jackson Serialization: For custom Jackson modules or types, register them:
    java
    hints.serialization().registerType(MyDto.class);
    hints.reflection().registerType(
        MyDto.class,
        hint -> hint.withMembers(
            MemberCategory.DECLARED_FIELDS,
            MemberCategory.INVOKE_DECLARED_CONSTRUCTORS
        )
    );
    Add Jackson mix-ins to reflection hints if used:
    java
    hints.reflection().registerType(MyMixIn.class);
  3. Jackson Modules: Ensure Jackson modules are on the classpath:
    xml
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
  • Spring Boot 3.0+对原生镜像有极佳的支持
  • 确保使用兼容的Spring Boot版本(3.0+)
  • 大多数Spring库会自动提供GraalVM提示
  • 启用Spring AOT处理进行测试
何时添加自定义RuntimeHints:
仅当需要注册自定义提示时,创建
RuntimeHintsRegistrar
实现:
java
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);
    }
}
在主应用类中注册:
java
@SpringBootApplication
@ImportRuntimeHints(MyRuntimeHints.class)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Spring Boot原生镜像常见问题:
  1. Logback配置:在
    application.properties
    中添加:
    properties
    # 在原生镜像中禁用Logback的关闭钩子
    logging.register-shutdown-hook=false
    如果使用自定义Logback配置,确保
    logback-spring.xml
    在资源目录中,并添加到
    RuntimeHints
    java
    hints.resources().registerPattern("logback-spring.xml");
    hints.resources().registerPattern("org/springframework/boot/logging/logback/*.xml");
  2. Jackson序列化:对于自定义Jackson模块或类型,注册它们:
    java
    hints.serialization().registerType(MyDto.class);
    hints.reflection().registerType(
        MyDto.class,
        hint -> hint.withMembers(
            MemberCategory.DECLARED_FIELDS,
            MemberCategory.INVOKE_DECLARED_CONSTRUCTORS
        )
    );
    如果使用Jackson mix-ins,将其添加到反射提示:
    java
    hints.reflection().registerType(MyMixIn.class);
  3. Jackson模块:确保Jackson模块在类路径中:
    xml
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

Quarkus

Quarkus

  • Quarkus is designed for native images with zero configuration in most cases
  • Use
    @RegisterForReflection
    annotation for reflection needs
  • Quarkus extensions handle GraalVM configuration automatically
Common Quarkus Native Image Tips:
  1. Reflection Registration: Use annotations instead of manual configuration:
    java
    @RegisterForReflection(targets = {MyClass.class, MyDto.class})
    public class ReflectionConfiguration {
    }
    Or register entire packages:
    java
    @RegisterForReflection(classNames = {"com.example.package.*"})
  2. Resource Inclusion: Add to
    application.properties
    :
    properties
    quarkus.native.resources.includes=config/*.json,templates/**
    quarkus.native.additional-build-args=--initialize-at-run-time=com.example.RuntimeClass
  3. Database Drivers: Ensure you're using Quarkus-supported JDBC extensions:
    xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
  4. Build-Time vs Runtime Initialization: Control initialization with:
    properties
    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. Container Image Build: Use Quarkus container-image extensions:
    properties
    quarkus.native.container-build=true
    quarkus.native.builder-image=mandrel
  • Quarkus专为原生镜像设计,大多数情况下无需配置
  • 使用
    @RegisterForReflection
    注解处理反射需求
  • Quarkus扩展会自动处理GraalVM配置
Quarkus原生镜像常见技巧:
  1. 反射注册:使用注解替代手动配置:
    java
    @RegisterForReflection(targets = {MyClass.class, MyDto.class})
    public class ReflectionConfiguration {
    }
    或者注册整个包:
    java
    @RegisterForReflection(classNames = {"com.example.package.*"})
  2. 资源包含:在
    application.properties
    中添加:
    properties
    quarkus.native.resources.includes=config/*.json,templates/**
    quarkus.native.additional-build-args=--initialize-at-run-time=com.example.RuntimeClass
  3. 数据库驱动:确保使用Quarkus支持的JDBC扩展:
    xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
  4. 构建时与运行时初始化:通过以下方式控制初始化时机:
    properties
    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容器镜像扩展:
    properties
    quarkus.native.container-build=true
    quarkus.native.builder-image=mandrel

Micronaut

Micronaut

  • Micronaut has built-in GraalVM support with minimal configuration
  • Use
    @ReflectionConfig
    and
    @Introspected
    annotations as needed
  • Micronaut's ahead-of-time compilation reduces reflection requirements
Common Micronaut Native Image Tips:
  1. Bean Introspection: Use
    @Introspected
    for POJOs to avoid reflection:
    java
    @Introspected
    public class MyDto {
        private String name;
        private int value;
        // getters and setters
    }
    Or enable package-wide introspection in
    application.yml
    :
    yaml
    micronaut:
      introspection:
        packages:
          - com.example.dto
  2. Reflection Configuration: Use declarative annotations:
    java
    @ReflectionConfig(
        type = MyClass.class,
        accessType = ReflectionConfig.AccessType.ALL_DECLARED_CONSTRUCTORS
    )
    public class MyConfiguration {
    }
  3. Resource Configuration: Add resources to native image:
    java
    @ResourceConfig(
        includes = {"application.yml", "logback.xml"}
    )
    public class ResourceConfiguration {
    }
  4. Native Image Configuration: In
    build.gradle
    :
    groovy
    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 Client Configuration: For Micronaut HTTP clients, ensure netty is properly configured:
    yaml
    micronaut:
      http:
        client:
          read-timeout: 30s
    netty:
      default:
        allocator:
          max-order: 3
  • Micronaut内置GraalVM支持,配置极少
  • 根据需要使用
    @ReflectionConfig
    @Introspected
    注解
  • Micronaut的提前编译减少了对反射的需求
Micronaut原生镜像常见技巧:
  1. Bean内省:对POJO使用
    @Introspected
    以避免反射:
    java
    @Introspected
    public class MyDto {
        private String name;
        private int value;
        // getter和setter
    }
    或者在
    application.yml
    中启用包级内省:
    yaml
    micronaut:
      introspection:
        packages:
          - com.example.dto
  2. 反射配置:使用声明式注解:
    java
    @ReflectionConfig(
        type = MyClass.class,
        accessType = ReflectionConfig.AccessType.ALL_DECLARED_CONSTRUCTORS
    )
    public class MyConfiguration {
    }
  3. 资源配置:将资源添加到原生镜像:
    java
    @ResourceConfig(
        includes = {"application.yml", "logback.xml"}
    )
    public class ResourceConfiguration {
    }
  4. 原生镜像配置:在
    build.gradle
    中:
    groovy
    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配置正确:
    yaml
    micronaut:
      http:
        client:
          read-timeout: 30s
    netty:
      default:
        allocator:
          max-order: 3

Best Practices

最佳实践

  • Start Simple: Build with
    --no-fallback
    to catch all native image issues
  • Use Tracing Agent: Run your application with the GraalVM tracing agent to automatically discover reflection, resources, and JNI requirements
  • Test Thoroughly: Native images behave differently than JVM applications
  • Minimize Reflection: Prefer compile-time code generation over runtime reflection
  • Profile Memory: Native images have different memory characteristics
  • CI/CD Integration: Add native image builds to your CI/CD pipeline
  • Keep Dependencies Updated: Use latest versions for better GraalVM compatibility
  • 从简开始:使用
    --no-fallback
    构建以捕获所有原生镜像问题
  • 使用追踪代理:运行应用时使用GraalVM追踪代理,自动发现反射、资源和JNI需求
  • 全面测试:原生镜像的行为与JVM应用不同
  • 减少反射使用:优先选择编译时代码生成而非运行时反射
  • 内存分析:原生镜像的内存特性与JVM不同
  • CI/CD集成:将原生镜像构建添加到CI/CD流水线
  • 保持依赖更新:使用最新版本以获得更好的GraalVM兼容性

Troubleshooting Tips

故障排除技巧

  1. Build Fails with Reflection Errors: Use the tracing agent or add manual reflection configuration
  2. Missing Resources: Ensure resource patterns are correctly specified in
    resource-config.json
  3. ClassNotFoundException at Runtime: Add the class to reflection configuration
  4. Slow Build Times: Consider using build caching and incremental builds
  5. Large Image Size: Use
    --gc=serial
    (default) or
    --gc=epsilon
    (no-op GC for testing) and analyze dependencies
  1. 构建因反射错误失败:使用追踪代理或添加手动反射配置
  2. 资源缺失:确保
    resource-config.json
    中的资源模式正确指定
  3. 运行时出现ClassNotFoundException:将该类添加到反射配置
  4. 构建时间过长:考虑使用构建缓存和增量构建
  5. 镜像体积过大:使用
    --gc=serial
    (默认)或
    --gc=epsilon
    (测试用无操作GC)并分析依赖

References

参考资料