使用 Native Image Gradle Plugin 集成 Reachability Metadata:从元数据仓库到 Tracing Agent 的完整实战指南
使用 Native Image Gradle Plugin 集成 Reachability Metadata从元数据仓库到 Tracing Agent 的完整实战指南【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址: https://gitcode.com/gh_mirrors/gr/graal本指南基于 GraalVM 仓库的官方文档编写讲解如何在 Gradle 构建中通过GraalVM Native Image Gradle Plugin为 Java 应用提供可达性元数据Reachability Metadata并最终构建出可运行的原生可执行文件。文中将以一个依赖 H2 数据库的 JDBC 应用为例完整演示使用 GraalVM Reachability Metadata Repository 自动下载元数据与使用 Tracing Agent 采集元数据两条主流路径帮助你理解二者差异并掌握元数据在构建期被解析、加载与校验的底层机制。为什么 Java 应用需要 Reachability MetadataJava 虚拟机的动态语言特性如反射、动态代理、JNI、类路径资源访问在运行时才会计算出被动态访问的程序元素字段、方法、类、资源 URL 等。在 HotSpot 上这没有问题因为所有类文件和资源在运行时都可用、可加载但代价是额外的内存与启动时间开销。为了让原生二进制足够小、启动足够快native-image构建器在构建期执行静态分析封闭世界假设只保留应用正确性所需的程序元素。然而静态分析无法推断出那些仅在运行时才可知的动态访问因此native-image要求开发者提供可达性元数据Reachability Metadata以声明这些动态访问的元素必须被包含进镜像。正如官方文档 Reachability Metadata 所述为构建器提供正确且完备的可达性元数据能保证应用正确性并确保第三方库在运行时的兼容性。在仓库源码中构建器通过ReachabilityMetadataResources选项定义于 ConfigurationFiles.java加载这些资源并支持通过--strict-configuration对应源码中的StrictConfiguration选项在配置文件不符合 schema 时直接中止构建而不是仅给出警告。元数据的几种提供方式你可以通过以下方式向native-image构建器提供可达性元数据详见 Reachability Metadata在代码中计算元数据给动态访问 API 传入常量参数例如Class.forName(Foo)构建期即可求值并存入原生二进制的初始堆或在构建期初始化类并把动态元素存入初始堆。通过 JSON 文件指定在类路径下的META-INF/native-image/groupId/artifactId/目录放置一个或多个reachability-metadata.json文件。使用-H:Preserveclasspath-selector参数直接保留指定的包、模块或类。使用 Feature API用于需要扫描类路径来计算正确元数据的高级场景。而在Gradle 构建中通常只需要掌握下面三种注入元数据的途径方式适用场景操作要点GraalVM Reachability Metadata Repository依赖了尚不支持 Native Image 的第三方库如 H2构建期插件自动下载元数据零配置Tracing Agent跟踪代理应用自身使用了反射、JNI、资源等动态特性在 JVM 上跑一遍应用agent 自动生成 JSON 配置资源自动探测Autodetecting所需资源直接位于类路径、src/main/resources/目录下插件自动识别无需额外操作本指南将重点演示前两种方式并用同一个 H2 JDBC 应用对比它们的差异。准备演示应用 H2Example为了体现真实世界应用的典型动态特性示例应用使用H2 数据库通过 JDBC 驱动访问这类场景恰好需要反射与资源类元数据。注意执行 Gradle 需要 Java 1721参见 Gradle 兼容性矩阵。如果你想用 Java 23或更高版本运行应用有一个变通方案将JAVA_HOME指向 1721 的 JDK将GRAALVM_HOME指向 GraalVM for JDK 23。详细说明见 Native Image Gradle Plugin 官方文档中Installing GraalVM Native Image tool一节。前置条件安装 GraalVM JDK。最简单的方式是通过 SDKMAN! 安装也可以从 GraalVM 官网下载。确保GRAALVM_HOME已正确设置。第一步创建项目骨架在 IDE 中新建一个名为H2Example的 Gradle 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 { withConnection(JDBC_CONNECTION_URL, connection - { connection.prepareStatement(DROP TABLE IF EXISTS customers).execute(); connection.commit(); }); SetString customers Set.of(Lord Archimonde, Arthur, Gilbert, Grug); System.out.println( Inserting the following customers in the database: ); printCustomers(customers); 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(); SetString savedCustomers new HashSet(); 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(SetString customers) { ListString 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; } }这段代码通过DriverManager.getConnection()动态建立 JDBC 连接内部会触发驱动类的反射加载——这正是原生镜像构建中最典型的元数据需求场景。第二步配置 build.gradle打开build.gradle替换为以下内容plugins { id application // 1. Native Image Gradle plugin id org.graalvm.buildtools.native version 0.10.3 } 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 { binaries { main { imageName.set(h2example) buildArgs.add(-Ob) } } }配置要点逐条说明启用 Native Image Gradle 插件版本号0.10.3。插件会自行发现需要传给native-image的 JAR 文件以及可执行文件的主类。显式指定应用主类避免插件误判。添加 H2 数据库依赖应用通过其 JDBC 驱动与数据库交互。H2 是一个开源 SQL 数据库也是默认不支持 Native Image的典型第三方库因此非常适合用来演示元数据仓库的价值。向native-image传递参数graalvmNative块中的buildArgs与命令行传参完全一致示例中的-Ob用于开启快速构建模式仅推荐开发期使用imageName.set(h2example)指定最终二进制的名字。更多配置项见插件官方文档的 Configuration 章节。第三步配置 settings.gradleNative Image Gradle 插件尚未发布到 Gradle Plugin Portal因此需要额外声明插件仓库。打开settings.gradle替换为pluginManagement { repositories { mavenCentral() gradlePluginPortal() } } rootProject.name H2Example include(H2Example)注意pluginManagement {}块必须出现在文件中任何其他语句之前。第四步可选先跑通 JVM 版本gradle run这会生成一个可运行的 JAR 并打印出数据库中存储的客户列表作为后续原生构建的基准行为。方式一使用 GraalVM Reachability Metadata RepositoryGraalVM Reachability Metadata Repository 为那些默认不支持 GraalVM Native Image的库提供 GraalVM 配置本应用的依赖H2 Database正是其中之一。使用此方式时Native Image Gradle 插件会在构建期自动从该仓库下载对应的元数据无需任何手工配置。用一条命令即可完成构建原生可执行文件 运行gradle nativeRun生成的原生可执行文件名为h2example位于build/native/nativeCompile目录该命令会直接从原生可执行文件运行应用输出与gradle run一致的客户列表。这一方式的本质是插件把从元数据仓库下载的reachability-metadata.json以META-INF/native-image/groupId/artifactId/的标准目录结构放入类路径使native-image构建器在构建期自动发现并加载对应源码中 ConfigurationFiles.java 所管理的ReachabilityMetadataResources资源机制。这正是文档 Reachability Metadata 所描述的 JSON 元数据规范在 Gradle 构建链中的落地。方式二使用 Tracing Agent第二种方式是在编译期注入 Tracing Agent跟踪代理让它在 JVM 运行应用时自动采集动态特性调用并生成 JSON 配置文件。理解 Agent 的三种运行模式模式行为推荐场景Standard标准无条件采集元数据构建原生可执行文件本指南采用Conditional条件带条件采集元数据基于typeReached为供进一步使用的原生共享库创建条件元数据Direct直接高级用户专用可直接控制传给 agent 的命令行高级/调试场景第一步配置 agent打开build.gradle在graalvmNative块中加入agent { defaultMode standard }defaultMode定义了 agent 的运行模式。如果你更习惯命令行传参等效写法是-Pagentstandard。第二步在 JVM 上带 agent 运行应用Native Image Gradle 插件约定向任何继承JavaForkOptions的 Gradle 任务例如test、run传递-Pagent即可启用 agentgradle -Pagent runagent 会捕获并记录本次运行中 H2 数据库调用及所有遇到的动态特性将结果写入build/native/agent-output/run目录下的 JSON 文件中。关于 agent 的采集机制Collecting Metadata Automatically 给出了底层细节agent 使用 JVM Tool InterfaceJVMTI跟踪动态特性访问在 JVM 退出时将元数据写成 JSON如果希望覆盖更多执行路径可以多次运行应用并用config-merge-dir选项把多次结果合并到既有配置集合中还可以用config-write-period-secs与config-write-initial-delay-secs让 agent 周期性而非仅退出时写出元数据。此外由于 agent 只观察已执行到的代码官方建议手动审查生成的配置文件并尽量让应用输入覆盖尽可能多的代码路径。第三步将元数据拷贝进项目使用插件提供的metadataCopy任务把采集到的元数据复制到项目的META-INF/native-image/目录gradle metadataCopy --task run --dir src/main/resources/META-INF/native-image输出目录虽然不是强制的但推荐使用src/main/resources/META-INF/native-image/因为native-image工具会自动从该位置读取元数据。关于如何为应用自动采集元数据的更多细节参见 Collecting Metadata Automatically。第四步基于 agent 采集的配置构建原生可执行文件gradle nativeRun该命令同样会先构建再直接运行原生可执行文件。第五步可选清理项目gradle clean并删除META-INF目录及其内容。深入理解agent 采集的元数据长什么样无论元数据来自仓库还是 agent最终都以 JSON 形式存在。下面结合 Reachability Metadata 的格式参考帮助你读懂 agent 生成的配置。一个reachability-metadata.json文件的顶层是包含若干数组的对象例如{ reflection: [], resources: [] }以反射为例一个类型条目可以包含以下常用字段字段类型说明typeString/Object类名、代理定义或 lambda 定义allDeclaredConstructorsBoolean启用对所有已声明构造器的访问allPublicConstructorsBoolean启用对所有公有构造器的访问allDeclaredMethodsBoolean启用对所有已声明方法的访问allPublicMethodsBoolean启用对所有公有方法含继承的访问allDeclaredFieldsBoolean启用对所有已声明字段的访问allPublicFieldsBoolean启用对所有公有字段含继承的访问methodsArray精确指定需要访问的方法nameparameterTypesfieldsArray精确指定需要访问的字段nameunsafeAllocatedBoolean允许通过Unsafe.allocateInstance()进行不安全分配此外reflection、jni、resources各节都支持condition字段typeReached用于条件注册只有当指定全限定类在运行时被触达时对应条目才生效。条件元数据可以避免原生二进制无谓膨胀是库维护者推荐采用的形式。关于元数据校验官方文档建议使用reachability-metadata-schema-v1.2.0.json位于仓库docs/reference-manual/native-image/assets/目录来校验配置若配置不符合 schema可通过--strict-configuration让构建直接失败而不是仅告警对应 ConfigurationFiles.java 中的StrictConfiguration选项。进阶agent 的过滤与注入技巧当自动采集的元数据包含过多无关内容时Collecting Metadata Automatically 提供了两类过滤机制Caller-based Filters调用方过滤按谁发起的访问过滤。内置过滤规则会排除源自 JVM 或 Native Image 直接支持的 Java 类库如java.nio的访问也可通过caller-filter-file提供自定义规则文件规则支持includeClasses/excludeClasses并可用.*仅当前包与.**当前包及所有子包通配。Access Filters访问目标过滤按访问的目标过滤例如排除com.example.internal.**包下所有类的元数据。两类过滤器可以组合使用。如果 Java 进程由应用或脚本启动、难以改动java命令行还可以通过JAVA_TOOL_OPTIONS环境变量注入 agent配合config-output-dir路径中的{pid}与{datetime}占位符避免多个并发进程互相覆盖输出。对于非类路径上的配置目录可用-H:ConfigurationFileDirectories直接指定类路径上但不在META-INF/native-image/下的目录则用-H:ConfigurationResourceRoots指定。总结本指南通过一个真实的 H2 JDBC 应用完整演示了在 Gradle 构建中集成 Reachability Metadata 的两种主流方式GraalVM Reachability Metadata Repository插件在构建期自动下载第三方库如 H2的元数据一条gradle nativeRun即可产出并运行原生可执行文件几乎零配置。它显著提升了 Native Image 对依赖第三方库的 Java 应用的可用性。Tracing Agent在 JVM 上运行应用时自动采集动态特性访问并生成 JSON 配置适合应用自身使用了反射、资源、JNI 等动态特性的场景配合metadataCopy任务可将元数据固化到项目源码中。两种方式的差异在于前者由社区维护的元数据仓库统一提供第三方库的配置省时省力后者采集的是你自己应用的精确行为覆盖面取决于运行时执行的代码路径因此建议多路径运行并人工复核生成的配置。结合 Reachability Metadata 与 Collecting Metadata Automatically 两篇参考文档以及仓库中 ConfigurationFiles.java 的实现可以进一步了解元数据的 JSON schema、条件注册语义与构建期加载机制为真实项目落地 Native Image 提供坚实依据。相关文档Reachability Metadata元数据类型、JSON 格式参考与条件注册语义Collecting Metadata AutomaticallyTracing Agent 的采集、过滤与合并机制Native Image Basics静态分析、封闭世界假设与构建期/运行期概念Build a Native Executable with Reflection反射场景的元数据入门指南【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址: https://gitcode.com/gh_mirrors/gr/graal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

FoundationDB Go 绑定(fdb-go)开发指南:安装、构建与事务编程实战

FoundationDB Go 绑定(fdb-go)开发指南:安装、构建与事务编程实战

FoundationDB Go 绑定(fdb-go)开发指南:安装、构建与事务编程实战 【免费下载链接】foundationdb FoundationDB - the open source, distributed, transactional key-value store 项目地址: https://gitcode.com/gh_mirrors/fo/foundationd…

2026/9/21 15:51:58 阅读更多 →
Moya 端点(Endpoint)深度指南:理解 Target 到 Endpoint 再到 URLRequest 的完整映射链路

Moya 端点(Endpoint)深度指南:理解 Target 到 Endpoint 再到 URLRequest 的完整映射链路

Moya 端点(Endpoint)深度指南:理解 Target 到 Endpoint 再到 URLRequest 的完整映射链路 【免费下载链接】Moya Network abstraction layer written in Swift. 项目地址: https://gitcode.com/gh_mirrors/mo/Moya Endpoint 是 Moya 中…

2026/9/21 15:51:58 阅读更多 →
做一套企业招聘系统,传统开发要7天,飞算JavaAI为什么15分钟就跑通了?

做一套企业招聘系统,传统开发要7天,飞算JavaAI为什么15分钟就跑通了?

一个中等复杂度的管理后台,传统开发通常会排出这样的时间:前端约3天、后端约2天、前后端联调约2天,加起来约7天。 这7天到底花在了哪里?同一套需求换成飞算JavaAI后,由一名Java后端从需求输入推进到前后端项目运行&…

2026/9/21 15:51:58 阅读更多 →

最新新闻

淘宝排名靠前技巧揭秘:3个源码级优化点,面试必问的底层逻辑

淘宝排名靠前技巧揭秘:3个源码级优化点,面试必问的底层逻辑

淘宝排名靠前技巧揭秘:3个源码级优化点,面试必问的底层逻辑 官方文档堆砌术语,读完还是不会用?这行混久了都知道,真正的硬核知识往往藏在底层实现里。今天不扯虚的,直接拆解淘宝搜索排名的核心逻辑。很多开发者在面试中被问倒,不是不懂业务,而是不懂…

2026/9/22 18:07:24 阅读更多 →
5分钟吃透Reveal源码,手写实现核心逻辑不踩坑

5分钟吃透Reveal源码,手写实现核心逻辑不踩坑

5分钟吃透Reveal源码,手写实现核心逻辑不踩坑 面试被问“Reveal.js 源码是怎么实现页面切换动画的”,你答得上来吗?别慌,很多后端转全栈的兄弟都栽在这。不是让你背代码,而是得懂那套 手写实现…

2026/9/22 18:07:24 阅读更多 →
3步手写实现quicksort,彻底告别排序崩溃焦虑

3步手写实现quicksort,彻底告别排序崩溃焦虑

3步手写实现quicksort,彻底告别排序崩溃焦虑 上周凌晨两点,线上接口突然超时,CPU飙到100%。翻日志一看,全是 java.lang.OutOfMemoryError 和递归栈溢出的 StackOverflowError…

2026/9/22 18:07:24 阅读更多 →
3招搞定爱在星光里性能瓶颈,图解原理告别StackTrace报错

3招搞定爱在星光里性能瓶颈,图解原理告别StackTrace报错

3招搞定爱在星光里性能瓶颈,图解原理告别StackTrace报错 凌晨两点,服务器报警响了,我抓起电脑一看,CPU飙到95%,日志里全是红色的StackTrace。这种报错一堆看不懂的情况,每个后端开发都经历过。别慌,今天咱们不聊虚的,直接…

2026/9/22 18:07:24 阅读更多 →
3CDAEMON乱码速查手册:从堆栈到源码的性能突围

3CDAEMON乱码速查手册:从堆栈到源码的性能突围

3CDAEMON乱码速查手册:从堆栈到源码的性能突围 面对满屏红色的 StackTrace,是不是感觉脑子瞬间宕机?尤其是当 3CDAEMON 相关的日志输出变成一堆 ? 或 �…

2026/9/22 18:07:24 阅读更多 →
CF人物模型底层逻辑拆解:版本升级API变更保姆级教程

CF人物模型底层逻辑拆解:版本升级API变更保姆级教程

CF人物模型底层逻辑拆解:版本升级API变更保姆级教程 版本升级后 API 全变了?别慌,CF人物系统的底层映射没变。 很多老哥在接手项目时,一跑代码就报错,参数对不上,对象引用丢失。 这篇保姆级教程,带你从内存堆栈角度,彻底搞懂 CF…

2026/9/22 18:06:23 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →