返回

使用跟踪代理配置 Native Image

要为使用 Java 反射、动态代理对象、JNI 或类路径资源的 Java 应用程序构建原生可执行文件,您应该向 native-image 工具提供 JSON 格式的元数据文件,或者在代码中预先计算元数据。

您可以手动创建配置文件,但更便捷的方法是使用跟踪代理(从现在起称为代理)生成配置。本指南演示如何使用该代理配置 native-image。当您在 JVM 上运行应用程序时,代理会自动为您生成配置。

要了解如何使用代码中预先计算的元数据构建原生可执行文件,请参阅文档

本指南中的示例应用程序使用了反射。native-image 工具只能部分检测通过 Java 反射 API 访问的应用程序元素。因此,您需要明确提供有关反射访问的类、方法和字段的详细信息。

无配置示例

前提条件

请确保您已安装 GraalVM JDK。最简单的入门方法是使用 SDKMAN!。有关其他安装选项,请访问下载部分

  1. 将以下源代码保存到名为 ReflectionExample.java 的文件中
     import java.lang.reflect.Method;
        
     class StringReverser {
         static String reverse(String input) {
             return new StringBuilder(input).reverse().toString();
         }
     }
        
     class StringCapitalizer {
         static String capitalize(String input) {
             return input.toUpperCase();
         }
     }
        
     public class ReflectionExample {
         public static void main(String[] args) throws ReflectiveOperationException {
             if (args.length == 0) {
                 System.err.println("You must provide the name of a class, the name of its method and input for the method");
                 return;
             }
             String className = args[0];
             String methodName = args[1];
             String input = args[2];
        
             Class<?> clazz = Class.forName(className);
             Method method = clazz.getDeclaredMethod(methodName, String.class);
             Object result = method.invoke(null, input);
             System.out.println(result);
         }
     }
    

    此 Java 应用程序使用命令行参数来确定要执行的操作。

  2. 编译示例,然后运行以下每个命令。
     javac ReflectionExample.java
    
     java ReflectionExample StringReverser reverse "hello"
    
     java ReflectionExample StringCapitalizer capitalize "hello"
    

    每个命令的输出应分别为 "olleh""HELLO"。(如果您提供任何其他字符串来标识类或方法,则会抛出异常。)

  3. 创建原生可执行文件,如下所示
     native-image ReflectionExample
    
  4. 使用以下命令运行生成的原生可执行文件
     ./reflectionexample StringReverser reverse "hello"
    

    您应该会看到一个异常,类似于

     Exception in thread "main" java.lang.ClassNotFoundException: StringReverser
         at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:190)
         ...
         at ReflectionExample.main(ReflectionExample.java:68)
    

    这表明,从其静态分析来看,native-image 工具无法确定应用程序使用了 StringReverser 类,因此未将其包含在原生可执行文件中。

有配置示例

以下步骤演示如何使用代理及其输出来创建依赖反射并需要配置的原生可执行文件。

  1. 在工作目录中创建一个名为 META-INF/native-image/ 的目录
     mkdir -p META-INF/native-image
    
  2. 启用代理运行应用程序,如下所示
     java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
    

    此命令会创建一个名为 rechability-metadata.json 的文件,其中包含类 StringReverser 及其 reverse() 方法的名称。

     {
     "reflection": [
         {
           "type": "StringReverser",
           "methods": [
             {
               "name": "reverse",
               "parameterTypes": [
                   "java.lang.String"
               ]
             }
           ]
         }
       ]
     }
    
  3. 构建原生可执行文件
     native-image ReflectionExample
    

    native-image 工具会自动使用 META-INF/native-image/ 目录中的元数据文件。但是,我们建议将 META-INF/native-image/ 目录放在类路径上,无论是通过 JAR 文件还是使用 -cp 选项。(这可以避免 IDE 用户因 IDE 本身定义的目录结构而产生混淆。)

  4. 测试您的可执行文件。
     ./reflectionexample StringReverser reverse "hello"
     olleh
    
     ./reflectionexample StringCapitalizer capitalize "hello"
    

    您应该会再次看到一个异常,类似于

     Exception in thread "main" java.lang.ClassNotFoundException: StringCapitalizer
         at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:190)
         ...
         at ReflectionExample.main(ReflectionExample.java:68)
    

    跟踪代理和 native-image 工具都无法确保配置文件是完整的。代理会观察并记录您运行应用程序时哪些程序元素通过反射进行访问。在此示例中,native-image 工具尚未配置为包含对 StringCapitalizer 类的引用。

  5. 更新配置以包含类 StringCapitalizer。您可以手动编辑 reachability-metadata.json 文件,或者重新运行跟踪代理以使用 config-merge-dir 选项更新现有配置文件,如下所示
     java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
    

    此命令会更新 reachability-metadata.json 文件,以包含类 StringCapitalizer 及其 capitalize() 方法的名称。

     {
       "reflection": [
         {
           "type": "StringCapitalizer",
           "methods": [
             {
               "name": "capitalize",
               "parameterTypes": [
                 "java.lang.String"
               ]
             }
           ]
         },
         {
           "type": "StringReverser",
           "methods": [
             {
               "name": "reverse",
               "parameterTypes": [
                 "java.lang.String"
               ]
             }
           ]
         }
       ]
     }
    
  6. 重建原生可执行文件并运行。
     native-image ReflectionExample
    
     ./reflectionexample StringCapitalizer capitalize "hello"
    

    应用程序现在应该能按预期工作。

联系我们