使用graalvm为带有反射功能的java代码生成native image的示例详解

 更新时间:2024年02月09日 09:16:35   作者:心与梦  
graalvm让native镜像支持反射的关键是利用json提前告诉它哪些类的哪些方法会被反射调用,然后它就能力在运行时支持反射了,这篇文章主要介绍了如何使用graalvm为带有反射功能的java代码生成native image,需要的朋友可以参考下

译自Configure Native Image with the Tracing Agent graal官方文档 , 以下所有命令需要在linux环境下操作,graalvm也支持windows。

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

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

若要了解如何使用代码中预先计算的元数据生成本机可执行文件,请参阅文档 - Reachability Metadata 可访问性元数据

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

不配置Json格式的反射信息示例

将以下源代码保存在名为 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 应用程序使用命令行参数来确定要执行的反射操作。

运行如下命令:

$JAVA_HOME/bin/javac ReflectionExample.java   # 编译
$JAVA_HOME/bin/java ReflectionExample StringReverser reverse "hello"   # 输出 olleh
$JAVA_HOME/bin/java ReflectionExample StringCapitalizer capitalize "hello"   # 输出 HELLO

使用 native-image 命令创建本机可执行文件,如下所示:

 $JAVA_HOME/bin/native-image --no-fallback ReflectionExample

使用以下命令运行生成的本机可执行文件:

./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 类是否被使用,所以未将其包含在本机可执行文件中。

配置Json格式的反射信息示例

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

在工作目录中创建名为 META-INF/native-image 的目录:

mkdir -p META-INF/native-image

在启用代理的情况下运行应用程序生成json配置,如下所示:

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"

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

 [
 {
 "name":"StringReverser",
 "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
 }
]

构建本机可执行文件:

$JAVA_HOME/bin/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 。您可以使用以下 config-merge-dir 选项手动编辑 reflect-config.json 文件或重新运行跟踪代理以更新现有配置文件,如下所示:

 $JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"

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

 [
     {
     "name":"StringCapitalizer",
     "methods":[{"name":"capitalize","parameterTypes":["java.lang.String"] }]
     },
     {
     "name":"StringReverser",
     "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
     }
 ]

重新生成本机可执行文件并运行,不会有报错了。

 $JAVA_HOME/bin/native-image ReflectionExample
 ./reflectionexample StringCapitalizer capitalize "hello"

总结

graalvm让native镜像支持反射的关键是利用json提前告诉它哪些类的哪些方法会被反射调用,然后它就能力在运行时支持反射了。

到此这篇关于如何使用graalvm为带有反射功能的java代码生成native image的文章就介绍到这了,更多相关graalvm 生成native image内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于java中byte数组与int类型的转换(两种方法)

    基于java中byte数组与int类型的转换(两种方法)

    下面小编就为大家带来一篇基于java中byte数组与int类型的转换(两种方法)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • java 读写Parquet格式的数据的示例代码

    java 读写Parquet格式的数据的示例代码

    本篇文章主要介绍了java 读写Parquet格式的数据的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • MyBatis关于二级缓存问题

    MyBatis关于二级缓存问题

    本篇文章主要介绍了MyBatis关于二级缓存问题,二级缓存是Mapper级别的缓存,多个sqlSession操作同一个Mapper,其二级缓存是可以共享的。
    2017-03-03
  • SpringMVC中的几个模型对象

    SpringMVC中的几个模型对象

    这篇文章主要介绍了SpringMVC中的几个模型对象,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • sharding-jdbc实现分页查询的示例代码

    sharding-jdbc实现分页查询的示例代码

    sharding-jdbc是一个轻量级Java框架,它提供了分布式数据库中间件的功能,支持水平分表和分库分表,在分页查询方面,sharding-jdbc支持两种方式:基于物理分页和基于逻辑分页,本文给大家介绍sharding-jdbc如何实现分页查询,需要的朋友可以参考下
    2024-05-05
  • SpringCloud使用Feign文件上传、下载

    SpringCloud使用Feign文件上传、下载

    这篇文章主要为大家详细介绍了SpringCloud使用Feign文件上传、下载功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • Myeclipse工程发布时端口占用问题的解决方法

    Myeclipse工程发布时端口占用问题的解决方法

    这篇文章主要介绍了Myeclipse工程发布时端口占用问题的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • java swing编程入门代码编写(java编程入门)

    java swing编程入门代码编写(java编程入门)

    Swing是一个为Java设计的GUI工具包,是用来做UI界面的,大家看了下面的介绍就要吧自己做java界面了
    2013-12-12
  • Java下载远程服务器文件到本地(基于http协议和ssh2协议)

    Java下载远程服务器文件到本地(基于http协议和ssh2协议)

    这篇文章主要介绍了Java下载远程服务器文件到本地的方法(基于http协议和ssh2协议),帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-01-01
  • Java中的异步回调问题

    Java中的异步回调问题

    这篇文章主要介绍了Java中的异步回调问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12

最新评论