◀返回
使用 Native Image Gradle 插件包含可达性元数据
您可以使用**Gradle**从 Java 应用程序构建本机可执行文件。为此,请使用作为 Native Build Tools 项目一部分提供的 GraalVM Native Image Gradle 插件。
一个“现实世界”的 Java 应用程序可能需要一些 Java 反射对象,或者调用一些本机代码,或者访问类路径上的资源 - 这些动态功能需要 native-image
工具在构建时了解,并以 元数据的形式提供。(Native Image 在构建时动态加载类,而不是在运行时加载。)
根据您的应用程序依赖项,有三种方法可以使用 Native Image Gradle 插件提供元数据
- 使用 GraalVM 可达性元数据存储库
- 使用跟踪代理
- 自动检测(如果所需的资源直接在类路径上可用,位于 src/main/resources/ 目录中)
本指南演示了如何使用 GraalVM 可达性元数据存储库和 跟踪代理构建本机可执行文件。本指南的目标是说明两种方法之间的区别,并演示如何使用可达性元数据可以简化您的开发任务。
我们建议您按照说明一步一步地创建应用程序。或者,您可以直接转到 完成的示例。
准备演示应用程序
注意:需要 Java 17 到 20 之间的版本才能执行 Gradle(请参见 Gradle 兼容性矩阵)。但是,如果您想使用 Java 21(或更高版本)运行应用程序,有一个变通方法:将
JAVA_HOME
设置为 Java 17 到 20 之间的版本,并将GRAALVM_HOME
设置为 JDK 21 的 GraalVM。有关更多详细信息,请参见 Native Image Gradle 插件文档。
先决条件
确保您已安装 GraalVM JDK。最简单的入门方法是使用 SDKMAN!。有关其他安装选项,请访问 下载部分。
-
在您喜欢的 IDE 中使用**Gradle**创建一个名为“H2Example”的新 Java 项目,位于
org.graalvm.example
包中。 - 将默认的 app/ 目录重命名为 H2Example/,然后将默认文件名 App.java 重命名为 H2Example.java,并将其内容替换为以下内容
package org.graalvm.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; public class H2Example { public static final String JDBC_CONNECTION_URL = "jdbc:h2:./data/test"; public static void main(String[] args) throws Exception { // Cleanup withConnection(JDBC_CONNECTION_URL, connection -> { connection.prepareStatement("DROP TABLE IF EXISTS customers").execute(); connection.commit(); }); Set<String> customers = Set.of("Lord Archimonde", "Arthur", "Gilbert", "Grug"); System.out.println("=== Inserting the following customers in the database: "); printCustomers(customers); // Insert data withConnection(JDBC_CONNECTION_URL, connection -> { connection.prepareStatement("CREATE TABLE customers(id INTEGER AUTO_INCREMENT, name VARCHAR)").execute(); PreparedStatement statement = connection.prepareStatement("INSERT INTO customers(name) VALUES (?)"); for (String customer : customers) { statement.setString(1, customer); statement.executeUpdate(); } connection.commit(); }); System.out.println(""); System.out.println("=== Reading customers from the database."); System.out.println(""); Set<String> savedCustomers = new HashSet<>(); // Read data withConnection(JDBC_CONNECTION_URL, connection -> { try (ResultSet resultSet = connection.prepareStatement("SELECT * FROM customers").executeQuery()) { while (resultSet.next()) { savedCustomers.add(resultSet.getObject(2, String.class)); } } }); System.out.println("=== Customers in the database: "); printCustomers(savedCustomers); } private static void printCustomers(Set<String> customers) { List<String> customerList = new ArrayList<>(customers); customerList.sort(Comparator.naturalOrder()); int i = 0; for (String customer : customerList) { System.out.println((i + 1) + ". " + customer); i++; } } private static void withConnection(String url, ConnectionCallback callback) throws SQLException { try (Connection connection = DriverManager.getConnection(url)) { connection.setAutoCommit(false); callback.run(connection); } } private interface ConnectionCallback { void run(Connection connection) throws SQLException; } }
-
删除 H2Example/src/test/java/ 目录(如果存在)。
- 打开 Gradle 配置文件 build.gradle,并将它的内容替换为以下内容
plugins { id 'application' // 1. Native Image Gradle plugin id 'org.graalvm.buildtools.native' version '0.10.1' } repositories { mavenCentral() } // 2. Application main class application { mainClass.set('org.graalvm.example.H2Example') } dependencies { // 3. H2 Database dependency implementation("com.h2database:h2:2.2.220") } // 4. Native Image build configuration graalvmNative { agent { defaultMode = "standard" } binaries { main { imageName.set('h2example') buildArgs.add("-Ob") } } }
1 启用 Native Image Gradle 插件。该插件会发现它需要传递给
native-image
的哪些 JAR 文件以及可执行文件的主类应该是哪个。2 显式指定应用程序主类。
3 添加对 H2 数据库(一个用于 Java 的开源 SQL 数据库)的依赖关系。应用程序通过 JDBC 驱动程序与该数据库交互。
4 您可以在
graalvmNative
插件配置中传递参数给native-image
工具。在单个buildArgs
中,您可以像在命令行中一样传递参数。-Ob
选项用于启用快速构建模式(仅推荐在开发期间使用)用作示例。imageName.set()
用于为生成的二进制文件指定名称。从 插件的文档中了解其他配置选项。 - 该插件尚未在 Gradle Plugin Portal 上可用,因此请声明一个额外的插件存储库。打开 settings.gradle 文件,并将默认内容替换为以下内容
pluginManagement { repositories { mavenCentral() gradlePluginPortal() } } rootProject.name = 'H2Example' include('H2Example')
注意,
pluginManagement {}
块必须出现在文件中的任何其他语句之前。 - (可选)构建应用程序。从存储库的根目录运行以下命令
./gradlew run
这会生成一个“可执行”JAR 文件,其中包含应用程序的所有依赖项,以及一个正确配置的 MANIFEST 文件。
使用 GraalVM 可达性元数据存储库构建本机可执行文件
Native Image Gradle 插件提供了对 GraalVM 可达性元数据存储库的支持。该存储库为默认情况下不支持 GraalVM Native Image 的库提供 GraalVM 配置。其中之一是该应用程序依赖的 H2 数据库。需要显式启用支持。
- 打开 build.gradle 文件,在
graalvmNative
插件配置中启用 GraalVM 可达性元数据存储库metadataRepository { enabled = true }
整个配置块应如下所示
graalvmNative { agent { defaultMode = "standard" } binaries { main { imageName.set('h2example') buildArgs.add("-Ob") } } metadataRepository { enabled = true } }
该插件会自动从存储库下载元数据。
- 现在使用元数据构建本机可执行文件
./gradlew nativeRun
这会为平台生成一个本机可执行文件,位于 build/native/nativeCompile/ 目录中,名为
h2example
。该命令还会从该本机可执行文件中运行应用程序。
使用 GraalVM 可达性元数据存储库增强了 Native Image 对依赖于第三方库的 Java 应用程序的可用性。
使用跟踪代理构建本机可执行文件
为 native-image
提供 medatata 配置的第二种方法是在编译时注入 跟踪代理(后面简称代理)。
该代理可以在三种模式下运行
- 标准:无条件收集元数据。如果您正在构建本机可执行文件,建议使用此模式。
- 条件:有条件地收集元数据。如果您要为打算进一步使用的本机共享库创建条件元数据,建议使用此模式。
- 直接:仅供高级用户使用。此模式允许直接控制传递给代理的命令行。
您可以通过传递命令行上的选项或在 build.gradle 文件中配置代理。请参见以下如何使用跟踪代理收集元数据,以及如何应用提供的配置构建本机可执行文件。
- 打开 build.gradle 文件,查看
graalvmNative
插件配置中指定的代理模式graalvmNative { agent { defaultMode = "standard" } ... }
如果您更喜欢命令行选项,它是
-Pagent=standard
。 - 现在在 JVM 上使用代理运行您的应用程序。要使用 Native Image Gradle 插件启用代理,请将
-Pagent
选项传递给扩展JavaForkOptions
的任何 Gradle 任务(例如test
或run
)./gradlew -Pagent run
该代理会捕获并记录对 H2 数据库的调用以及在测试运行期间遇到的所有动态功能,并将这些记录写入多个 *-config.json 文件中。
- 收集元数据后,使用
metadataCopy
任务将其复制到项目的 /META-INF/native-image/ 目录中./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image
输出目录为 /resources/META-INF/native-image/ 不是必需的,但建议这样做。
native-image
工具会自动从该位置获取元数据。有关如何自动为您的应用程序收集元数据的更多信息,请参见 自动收集元数据。 - 使用代理收集的配置构建本机可执行文件
./gradlew nativeCompile
名为 h2example 的本机可执行文件将在 build/native/nativeCompile 目录中创建。
- 从本机可执行文件中运行应用程序
./build/native/nativeCompile/h2example
- (可选)要清理项目,请运行
./gradlew clean
,并删除包含其内容的 META-INF 目录。
总结
本指南演示了如何使用 GraalVM 可达性元数据存储库和跟踪代理构建本机可执行文件。目标是展示它们之间的区别,并证明使用可达性元数据可以简化工作。
注意,如果您的应用程序在运行时没有调用任何动态功能,则启用 GraalVM 可达性元数据存储库是没有必要的。在这种情况下,您的工作流程将是
./gradlew nativeRun