◀返回
使用跟踪代理配置原生镜像
要为使用 Java 反射、动态代理对象、JNI 或类路径资源的 Java 应用程序构建原生可执行文件,您应该向 native-image
工具提供以 JSON 格式的元数据文件,或者在代码中预计算元数据。
您可以手动创建配置文件,但更方便的方法是使用跟踪代理(从现在开始称为代理)生成配置。本指南演示如何使用代理配置 native-image
。代理在您在 JVM 上运行应用程序时自动为您生成配置。
要了解如何在代码中预计算元数据的情况下构建原生可执行文件,请参阅文档。
本指南中的示例应用程序使用 Java 反射。native-image
工具仅部分检测使用 Java 反射 API 访问的应用程序元素。因此,您需要为它提供有关反射访问的类、方法和字段的详细信息。
无配置示例
以下应用程序演示了 Java 反射的使用。
先决条件
确保您已安装 GraalVM JDK。最简单的入门方法是使用 SDKMAN!。有关其他安装选项,请访问 下载部分。
- 将以下源代码保存在名为 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 应用程序使用命令行参数来确定要执行的操作。
- 编译示例,然后运行以下每个命令。
javac ReflectionExample.java
java ReflectionExample StringReverser reverse "hello"
java ReflectionExample StringCapitalizer capitalize "hello"
每个命令的输出应分别为
"olleh"
和"HELLO"
。(如果您提供任何其他字符串来标识类或方法,则会抛出异常。) - 创建原生可执行文件,如下所示
native-image --no-fallback ReflectionExample
注意:
native-image
的--no-fallback
选项会导致实用程序在无法创建可执行文件时失败。 - 使用以下命令运行生成的原生可执行文件
./reflectionexample StringReverser reverse "hello"
您应该看到类似于以下内容的异常
Exception in thread "main" java.lang.ClassNotFoundException: StringReverser at java.lang.Class.forName(DynamicHub.java:1338) at java.lang.Class.forName(DynamicHub.java:1313) at ReflectionExample.main(ReflectionExample.java:25)
这表明,从其静态分析中,
native-image
工具无法确定类StringReverser
被应用程序使用,因此没有将其包含在原生可执行文件中。
带配置的示例
以下步骤演示如何使用代理及其输出创建依赖于反射并需要配置的原生可执行文件。
- 在工作目录中创建一个名为 META-INF/native-image/ 的目录
mkdir -p META-INF/native-image
- 启用代理运行应用程序,如下所示
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
此命令创建一个名为 reachability-metadata.json 的文件,其中包含类
StringReverser
及其reverse()
方法的名称。{ "reflection": [ { "type":"StringReverser", "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }] } ] }
- 构建原生可执行文件
native-image ReflectionExample
native-image
工具会自动使用 META-INF/native-image/ 目录中的元数据文件。但是,我们建议 META-INF/native-image/ 目录在类路径上,无论是通过 JAR 文件还是使用-cp
选项。(这避免了 IDE 用户的混淆,因为目录结构由 IDE 本身定义。) - 测试您的可执行文件。
./reflectionexample StringReverser reverse "hello" olleh
./reflectionexample StringCapitalizer capitalize "hello"
您应该再次看到类似于以下内容的异常
Exception in thread "main" java.lang.ClassNotFoundException: StringCapitalizer at java.lang.Class.forName(DynamicHub.java:1338) at java.lang.Class.forName(DynamicHub.java:1313) at ReflectionExample.main(ReflectionExample.java:25)
跟踪代理或
native-image
工具都无法确保配置文件完整。代理会观察并记录您运行程序时使用反射访问的程序元素。在本例中,native-image
工具尚未配置为包含对类StringCapitalizer
的引用。 - 更新配置以包含类
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"] }] } ] }
- 重新构建原生可执行文件并运行它。
native-image ReflectionExample
./reflectionexample StringCapitalizer capitalize "hello"
应用程序现在应该按预期工作。