GraalVM 原生映像支持
1. GraalVM 原生镜像简介
GraalVM 本机映像提供了一种部署和运行 Java 应用程序的新方法。 与 Java 虚拟机相比,本机映像可以以更小的内存占用和更快的启动时间运行。
它们非常适合使用容器映像部署的应用程序,并且与“功能即服务”(FaaS) 平台结合使用时特别有趣。
与为 JVM 编写的传统应用程序不同,GraalVM 本机映像应用程序需要提前处理才能创建可执行文件。 这种提前处理涉及从其主入口点静态分析您的应用程序代码。
GraalVM 本机映像是一个完整的、特定于平台的可执行文件。 您无需提供 Java 虚拟机即可运行本机映像。
| 如果您只想开始并试验 GraalVM,您可以跳到“开发您的第一个 GraalVM 本机应用程序”部分,稍后返回到本部分。 |
1.1. 与 JVM 部署的主要区别
GraalVM 本机映像是提前生成的,这意味着本机应用程序和基于 JVM 的应用程序之间存在一些关键差异。 主要区别是:
-
应用程序的静态分析在构建时从
main入口点。 -
创建本机映像时无法访问的代码将被删除,并且不会成为可执行文件的一部分。
-
GraalVM 不直接了解代码的动态元素,必须被告知反射、资源、序列化和动态代理。
-
应用程序类路径在构建时是固定的,不能更改。
-
没有延迟类加载,可执行文件中提供的所有内容都将在启动时加载到内存中。
-
Java 应用程序的某些方面存在一些不完全支持的限制。
除了这些差异之外,Spring 还使用了一种称为 Spring Ahead-of-Time 处理的过程,这会施加进一步的限制。 请务必至少阅读下一节的开头以了解这些内容。
| GraalVM 参考文档的“本机映像兼容性指南”部分提供了有关 GraalVM 限制的更多详细信息。 |
1.2. 了解 Spring 提前处理
典型的 Spring Boot 应用程序是非常动态的,配置在运行时执行。 事实上,Spring Boot 自动配置的概念在很大程度上取决于对运行时状态的反应,以便正确配置事物。
尽管可以告诉 GraalVM 应用程序的这些动态方面,但这样做会抵消静态分析的大部分好处。 因此,相反,当使用 Spring Boot 创建本机映像时,假设是一个封闭的世界,并且应用程序的动态方面受到限制。
除了 GraalVM 本身创建的限制外,封闭世界假设还意味着以下限制:
-
应用程序中定义的 bean 不能在运行时更改,这意味着:
-
Spring
@Profile注释和特定于配置文件的配置有限制。 -
不支持在创建 Bean 时更改的属性(例如,
@ConditionalOnProperty和.enable属性)。
-
当这些限制到位时,Spring 可以在构建期间执行提前处理并生成 GraalVM 可以使用的其他资产。 Spring AOT 处理的应用程序通常会生成:
-
Java 源代码
-
字节码(用于动态代理等)
-
GraalVM JSON 提示文件:
-
资源提示 (
resource-config.json) -
反射提示 (
reflect-config.json) -
序列化提示 (
serialization-config.json) -
Java 代理提示 (
proxy-config.json) -
JNI 提示 (
jni-config.json)
-
1.2.1. 源代码生成
Spring 应用程序由 Spring Beans 组成。 在内部,Spring Framework 使用两个不同的概念来管理 bean。 有 bean 实例,它们是已经创建的实际实例,可以注入到其他 bean 中。 还有一些 bean 定义用于定义 bean 的属性以及如何创建其实例。
如果我们采用典型的@Configuration类:
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
Bean 定义是通过解析@Configuration类并找到@Bean方法。
在上面的示例中,我们定义了一个BeanDefinition对于名为myBean.
我们还在创建一个BeanDefinition对于MyConfiguration类本身。
当myBean实例是必需的,Spring 知道它必须调用myBean()方法并使用结果。
在 JVM 上运行时,@Configuration类解析发生在应用程序启动时,并且@Bean使用反射调用方法。
创建原生映像时,Spring 以不同的方式运行。
而不是解析@Configuration类并在运行时生成 Bean 定义,它在构建时执行。
一旦发现了 bean 定义,它们就会被处理并转换为可以由 GraalVM 编译器分析的源代码。
Spring AOT 进程会将上面的配置类转换为如下代码:
/**
* Bean definitions for {@link MyConfiguration}.
*/
public class MyConfiguration__BeanDefinitions {
/**
* Get the bean definition for 'myConfiguration'.
*/
public static BeanDefinition getMyConfigurationBeanDefinition() {
Class<?> beanType = MyConfiguration.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(MyConfiguration::new);
return beanDefinition;
}
/**
* Get the bean instance supplier for 'myBean'.
*/
private static BeanInstanceSupplier<MyBean> getMyBeanInstanceSupplier() {
return BeanInstanceSupplier.<MyBean>forFactoryMethod(MyConfiguration.class, "myBean")
.withGenerator((registeredBean) -> registeredBean.getBeanFactory().getBean(MyConfiguration.class).myBean());
}
/**
* Get the bean definition for 'myBean'.
*/
public static BeanDefinition getMyBeanBeanDefinition() {
Class<?> beanType = MyBean.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(getMyBeanInstanceSupplier());
return beanDefinition;
}
}
| 生成的确切代码可能会有所不同,具体取决于 Bean 定义的性质。 |
您可以在上面看到,生成的代码创建了与@Configuration类,但以 GraalVM 可以理解的直接方式。
有一个 bean 定义myConfigurationbean,一个用于myBean.
当myBeaninstance 是必需的,则BeanInstanceSupplier被称为。
此提供商将调用myBean()方法myConfiguration豆。
| 在 Spring AOT 处理期间,您的应用程序将启动到 bean 定义可用的程度。 在 AOT 处理阶段不会创建 Bean 实例。 |
Spring AOT 将为您的所有 bean 定义生成这样的代码。
当需要 bean 后处理时,它还会生成代码(例如,调用@Autowired方法)。
一ApplicationContextInitializer还将生成,Spring Boot 将使用它来初始化ApplicationContext当实际运行 AOT 处理的应用程序时。
尽管 AOT 生成的源代码可能很冗长,但它非常可读,并且在调试应用程序时很有帮助。
生成的源文件可以在target/spring-aot/main/sources使用 Maven 和build/generated/aotSources与 Gradle 一起。 |
1.2.2. 提示文件生成
除了生成源文件外,Spring AOT 引擎还将生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,这些数据描述了 GraalVM 应如何通过直接检查代码来处理它无法理解的内容。
例如,您可能在私有方法上使用 Spring 注释。 Spring 需要使用反射来调用私有方法,即使在 GraalVM 上也是如此。 当出现这种情况时,Spring 可以编写反射提示,以便 GraalVM 知道即使私有方法没有直接调用,它仍然需要在本机镜像中可用。
提示文件在META-INF/native-imageGraalVM 会自动获取它们。
生成的提示文件可以在target/spring-aot/main/resources使用 Maven 和build/generated/aotResources与 Gradle 一起。 |
2. 开发您的第一个 GraalVM 原生应用程序
现在我们已经很好地了解了 GraalVM 本机映像以及 Spring 提前引擎的工作原理,我们可以了解如何创建应用程序。
构建 Spring Boot 原生镜像应用程序有两种主要方法:
-
使用对云原生 Buildpack 的 Spring Boot 支持来生成包含本机可执行文件的轻量级容器。
-
使用 GraalVM 本机构建工具生成本机可执行文件。
启动新的原生 Spring Boot 项目的最简单方法是转到 start.spring.io,添加“GraalVM Native Support”依赖项并生成项目。
包括HELP.mdfile 将提供入门提示。 |
2.1. 示例应用程序
我们需要一个示例应用程序,可用于创建我们的本机映像。 就我们的目的而言,“getting-started.html”部分中介绍的简单“Hello World!” Web 应用程序就足够了。
回顾一下,我们的主要应用程序代码如下所示:
@RestController
@SpringBootApplication
public class MyApplication {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
此应用程序使用 Spring MVC 和嵌入式 Tomcat,两者都经过测试和验证,可以与 GraalVM 本机映像配合使用。
2.2. 使用构建包构建原生镜像
Spring Boot 包括对 Maven 和 Gradle 的原生映像的构建包支持。 这意味着您只需键入一个命令,即可快速将合理的映像导入本地运行的 Docker 守护进程中。 生成的镜像不包含 JVM,而是静态编译原生镜像。 这会导致图像变小。
用于图像的构建器是paketobuildpacks/builder-jammy-tiny:latest.
它占用空间小,攻击面减少,但您也可以使用paketobuildpacks/builder-jammy-base:latest或paketobuildpacks/builder-jammy-full:latest如果需要,在映像中提供更多可用工具。 |
2.2.1. 系统要求
应安装 Docker。有关更多详细信息,请参阅获取 Docker。如果您使用的是 Linux,请将其配置为允许非 root 用户。
您可以运行docker run hello-world(没有sudo) 以检查 Docker 守护进程是否按预期访问。
有关更多详细信息,请查看 Maven 或 Gradle Spring Boot 插件文档。 |
在 macOS 上,建议将分配给 Docker 的内存增加到至少8GB,并可能添加更多 CPU。
有关更多详细信息,请参阅此 Stack Overflow 答案。
在 Microsoft Windows 上,请确保启用 Docker WSL 2 后端以获得更好的性能。 |
2.2.2. 使用 Maven
要使用 Maven 构建原生镜像容器,您应该确保您的pom.xml文件使用spring-boot-starter-parent和org.graalvm.buildtools:native-maven-plugin.
您应该有一个<parent>部分如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.12</version>
</parent>
您还应该在<build> <plugins>部分:
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
这spring-boot-starter-parent声明一个native配置文件,用于配置创建本机映像所需运行的执行。
您可以使用-P标志。
如果您不想使用spring-boot-starter-parent您需要为process-aot目标来自 Spring Boot 插件和add-reachability-metadata目标。 |
要构建映像,您可以运行spring-boot:build-image目标与native配置文件活动:
$ mvn -Pnative spring-boot:build-image
2.2.3. 使用 Gradle
应用 GraalVM Native Image 插件时,Spring Boot Gradle 插件会自动配置 AOT 任务。
您应该检查您的 Gradle build 是否包含plugins块,包括org.graalvm.buildtools.native.
只要org.graalvm.buildtools.native插件,则bootBuildImage任务将生成一个本机镜像,而不是一个 JVM 镜像。
您可以使用以下命令运行任务:
$ gradle bootBuildImage
2.2.4. 运行示例
运行适当的构建命令后,Docker 映像应该可用。
您可以使用以下命令启动您的应用程序docker run:
$ docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT
您应该会看到类似于以下内容的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.12)
....... . . .
....... . . . (log output here)
....... . . .
........ Started MyApplication in 0.08 seconds (process running for 0.095)
| 启动时间因机器而异,但它应该比在 JVM 上运行的 Spring Boot 应用程序快得多。 |
如果您打开 Web 浏览器以localhost:8080,您应该会看到以下输出:
Hello World!
要正常退出应用程序,请按ctrl-c.
2.3. 使用本机构建工具构建本机镜像
如果要在不使用 Docker 的情况下直接生成本机可执行文件,可以使用 GraalVM 本机构建工具。 本机构建工具是 GraalVM 为 Maven 和 Gradle 提供的插件。 您可以使用它们来执行各种 GraalVM 任务,包括生成本机映像。
2.3.1. 前提条件
要使用本机构建工具构建本机映像,您需要在计算机上安装 GraalVM 发行版。 您可以在 Liberica Native Image Kit 页面上手动下载,也可以使用 SDKMAN!
Linux 和 macOS
要在 macOS 或 Linux 上安装原生镜像编译器,我们建议使用 SDKMAN!。 获取 SDKMAN!从 sdkman.io,然后使用以下命令安装 Liberica GraalVM 发行版:
$ sdk install java 22.3.r17-nik
$ sdk use java 22.3.r17-nik
通过检查java -version:
$ java -version
openjdk version "17.0.5" 2022-10-18 LTS
OpenJDK Runtime Environment GraalVM 22.3.0 (build 17.0.5+8-LTS)
OpenJDK 64-Bit Server VM GraalVM 22.3.0 (build 17.0.5+8-LTS, mixed mode)
窗户
在 Windows 上,按照以下说明在版本 22.3 中安装 GraalVM 或 Liberica 本机映像工具包、Visual Studio 生成工具和 Windows SDK。 由于与 Windows 相关的命令行最大长度,请确保使用 x64 本机工具命令提示符而不是常规 Windows 命令行来运行 Maven 或 Gradle 插件。
2.3.2. 使用 Maven
与 buildpack 支持一样,您需要确保使用spring-boot-starter-parent为了继承nativeprofile 和org.graalvm.buildtools:native-maven-plugin插件。
使用nativeprofile active,您可以调用native:compile要触发的目标native-image汇编:
$ mvn -Pnative native:compile
本机映像可执行文件可以在target目录。
2.3.3. 使用 Gradle
当 Native Build Tools Gradle 插件应用于您的项目时,Spring Boot Gradle 插件将自动触发 Spring AOT 引擎。
任务依赖项是自动配置的,因此您只需运行标准nativeCompile生成原生镜像的任务:
$ gradle nativeCompile
本机映像可执行文件可以在build/native/nativeCompile目录。
2.3.4. 运行示例
此时,您的应用程序应该可以工作。您现在可以通过直接运行应用程序来启动它:
$ target/myproject
$ build/native/nativeCompile/myproject
您应该会看到类似于以下内容的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.12)
....... . . .
....... . . . (log output here)
....... . . .
........ Started MyApplication in 0.08 seconds (process running for 0.095)
| 启动时间因机器而异,但它应该比在 JVM 上运行的 Spring Boot 应用程序快得多。 |
如果您打开 Web 浏览器以localhost:8080,您应该会看到以下输出:
Hello World!
要正常退出应用程序,请按ctrl-c.
3. 测试 GraalVM 本机镜像
在编写本机映像应用程序时,我们建议您尽可能继续使用 JVM 来开发大部分单元和集成测试。 这将有助于缩短开发人员的构建时间,并允许您使用现有的 IDE 集成。 通过对 JVM 的广泛测试覆盖,您可以将本机图像测试集中在可能不同的区域。
对于原生映像测试,您通常希望确保以下方面有效:
-
Spring AOT 引擎能够处理您的应用程序,它将在 AOT 处理模式下运行。
-
GraalVM 有足够的提示来确保可以生成有效的本机映像。
3.1. 使用 JVM 测试提前处理
当 Spring Boot 应用程序运行时,它会尝试检测它是否作为本机映像运行。 如果它作为本机映像运行,它将使用 Spring AOT 引擎在构建时生成的代码初始化应用程序。
如果应用程序在常规 JVM 上运行,则将忽略任何 AOT 生成的代码。
由于native-image编译阶段可能需要一段时间才能完成,有时在 JVM 上运行应用程序但让它使用 AOT 生成的初始化代码很有用。
这样做有助于快速验证 AOT 生成的代码中是否没有错误,并且在应用程序最终转换为本机映像时是否缺少任何内容。
要在 JVM 上运行 Spring Boot 应用程序并让它使用 AOT 生成的代码,您可以将spring.aot.enabled系统属性设置为true.
例如:
$ java -Dspring.aot.enabled=true -jar myapplication.jar
您需要确保您正在测试的 jar 包含 AOT 生成的代码。
对于 Maven,这意味着您应该使用-Pnative激活native轮廓。
对于 Gradle,您需要确保您的构建包含org.graalvm.buildtools.native插件。 |
如果您的应用程序以spring.aot.enabled属性设置为true,那么您更有信心将其转换为原生图像时能够正常工作。
您还可以考虑针对正在运行的应用程序运行集成测试。
例如,您可以使用 SpringWebClient调用应用程序 REST 端点。
或者,您可以考虑使用像 Selenium 这样的项目来检查应用程序的 HTML 响应。
3.2. 使用本机构建工具进行测试
GraalVM 本机构建工具包括在本机映像中运行测试的功能。 当您想要深入测试应用程序的内部结构是否在 GraalVM 本机映像中工作时,这会很有帮助。
生成包含要运行的测试的本机映像可能是一项耗时的作,因此大多数开发人员可能更喜欢在本地使用 JVM。 但是,它们作为 CI 管道的一部分非常有用。 例如,您可以选择每天运行一次本机测试。
Spring Framework 包括对运行测试的预先支持。
所有常见的 Spring 测试功能都适用于本机映像测试。
例如,您可以继续使用@SpringBootTest注解。
您还可以使用 Spring Boot 测试切片仅测试应用程序的特定部分。
Spring Framework 的本机测试支持以以下方式工作:
-
分析测试以发现任何
ApplicationContext将需要的实例。 -
提前处理将应用于每个应用程序上下文并生成资产。
-
将创建一个本机映像,生成的资产由 GraalVM 处理。
-
本机映像还包括 JUnit
TestEngine配置了已发现测试的列表。 -
启动本机映像,触发引擎,该引擎将运行每个测试并报告结果。
3.2.1. 使用 Maven
要使用 Maven 运行本机测试,请确保您的pom.xml文件使用spring-boot-starter-parent.
您应该有一个<parent>部分如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.12</version>
</parent>
这spring-boot-starter-parent声明一个nativeTest配置文件,用于配置运行本机测试所需的执行。
您可以使用-P标志。
如果您不想使用spring-boot-starter-parent您需要为process-test-aot目标和test目标。 |
要构建映像并运行测试,请使用test目标与nativeTest配置文件活动:
$ mvn -PnativeTest test
4. 高级原生图像主题
4.1. 嵌套配置属性
Spring 提前引擎会自动为配置属性创建反射提示。
但是,非内部类的嵌套配置属性必须使用@NestedConfigurationProperty,否则它们将不会被检测到并且无法绑定。
@ConfigurationProperties(prefix = "my.properties")
public class MyProperties {
private String name;
@NestedConfigurationProperty
private final Nested nested = new Nested();
}
哪里Nested是:
public class Nested {
private int number;
}
上面的示例生成了my.properties.name和my.properties.nested.number.
如果没有@NestedConfigurationProperty注释nested字段,my.properties.nested.number属性在本机映像中不可绑定。
使用构造函数绑定时,您必须使用@NestedConfigurationProperty:
@ConfigurationProperties(prefix = "my.properties")
public class MyPropertiesCtor {
private final String name;
@NestedConfigurationProperty
private final Nested nested;
public MyPropertiesCtor(String name, Nested nested) {
this.name = name;
this.nested = nested;
}
}
使用记录时,必须使用@NestedConfigurationProperty:
@ConfigurationProperties(prefix = "my.properties")
public record MyPropertiesRecord(String name, @NestedConfigurationProperty Nested nested) {
}
使用 Kotlin 时,您需要使用@NestedConfigurationProperty:
@ConfigurationProperties(prefix = "my.properties")
data class MyPropertiesKotlin(
val name: String,
@NestedConfigurationProperty val nested: Nested
)
| 请在所有情况下使用公共 getter 和 setter,否则属性将不可绑定。 |
4.2. 转换 Spring Boot 可执行 jar
只要 jar 包含 AOT 生成的资产,就可以将 Spring Boot 可执行 jar 转换为本机映像。 这可能出于多种原因而有用,包括:
-
您可以保留常规的 JVM 管道,并将 JVM 应用程序转换为 CI/CD 平台上的本机映像。
-
如
native-image不支持交叉编译,您可以保留作系统中立的部署项目,稍后将其转换为不同的作系统体系结构。
您可以使用 Cloud Native Buildpacks 或使用native-imageGraalVM 附带的工具。
| 可执行 jar 必须包含 AOT 生成的资产,例如生成的类和 JSON 提示文件。 |
4.2.1. 使用 Buildpack
Spring Boot 应用程序通常通过 Maven (mvn spring-boot:build-image) 或 Gradle (gradle bootBuildImage) 集成。
但是,您也可以使用pack将 AOT 处理的 Spring Boot 可执行 jar 转换为本机容器映像。
首先,确保 Docker 守护进程可用(有关更多详细信息,请参阅获取 Docker)。如果您使用的是 Linux,请将其配置为允许非 root 用户。
您还需要安装pack按照 buildpacks.io 上的安装指南进行作。
假设 AOT 处理的 Spring Boot 可执行 jar 构建为myproject-0.0.1-SNAPSHOT.jar在target目录下,运行:
$ pack build --builder paketobuildpacks/builder-jammy-tiny \
--path target/myproject-0.0.1-SNAPSHOT.jar \
--env 'BP_NATIVE_IMAGE=true' \
my-application:0.0.1-SNAPSHOT
| 您无需安装本地 GraalVM 即可以这种方式生成映像。 |
一次pack已完成,您可以使用docker run:
$ docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT
4.2.2. 使用 GraalVM 原生映像
将 AOT 处理的 Spring Boot 可执行文件 jar 转换为本机可执行文件的另一个选项是使用 GraalVMnative-image工具。
为此,您需要在计算机上安装 GraalVM 发行版。
您可以在 Liberica Native Image Kit 页面上手动下载它,也可以使用 SDKMAN!
假设 AOT 处理的 Spring Boot 可执行 jar 构建为myproject-0.0.1-SNAPSHOT.jar在target目录下,运行:
$ rm -rf target/native
$ mkdir -p target/native
$ cd target/native
$ jar -xvf ../myproject-0.0.1-SNAPSHOT.jar
$ native-image -H:Name=myproject @META-INF/native-image/argfile -cp .:BOOT-INF/classes:`find BOOT-INF/lib | tr '\n' ':'`
$ mv myproject ../
| 这些命令适用于 Linux 或 macOS 计算机,但您需要将它们调整为 Windows。 |
这@META-INF/native-image/argfile可能没有打包在您的 jar 中。
仅当需要可访问性元数据覆盖时,才会包含它。 |
这native-image -cp标志不接受通配符。
您需要确保列出所有 jar(上面的命令使用find和tr来做到这一点)。 |
4.3. 使用跟踪代理
GraalVM 本机映像跟踪代理允许您拦截 JVM 上的反射、资源或代理使用情况,以生成相关提示。 Spring 应该会自动生成其中的大部分提示,但跟踪代理可用于快速识别缺失的条目。
使用代理为本机图像生成提示时,有几种方法:
-
直接启动应用程序并进行练习。
-
运行应用程序测试以练习应用程序。
第一个选项对于识别 Spring 无法识别库或模式时缺少的提示很有趣。
第二个选项对于可重复的设置听起来更有吸引力,但默认情况下,生成的提示将包括测试基础设施所需的任何内容。 当应用程序实际运行时,其中一些将是不必要的。 为了解决这个问题,代理支持一个访问过滤器文件,该文件将导致某些数据从生成的输出中排除。
4.3.1. 直接启动应用程序
使用以下命令启动附加了本机图像跟踪代理的应用程序:
$ java -Dspring.aot.enabled=true \
-agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ \
-jar target/myproject-0.0.1-SNAPSHOT.jar
现在,您可以练习想要获得提示的代码路径,然后使用ctrl-c.
在应用程序关闭时,本机图像跟踪代理会将提示文件写入给定的配置输出目录。
您可以手动检查这些文件,也可以将它们用作本机映像生成过程的输入。
要将它们用作输入,请将它们复制到src/main/resources/META-INF/native-image/目录。
下次构建本机映像时,GraalVM 将考虑这些文件。
可以在本机图像跟踪代理上设置更高级的选项,例如按调用方类过滤记录的提示等。 如需进一步阅读,请参阅官方文档。
4.4. 自定义提示
如果您需要为反射、资源、序列化、代理使用等提供自己的提示,您可以使用RuntimeHintsRegistrar应用程序接口。
创建一个实现RuntimeHintsRegistrar接口,然后对提供的RuntimeHints实例:
public class MyRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// Register method for reflection
Method method = ReflectionUtils.findMethod(MyClass.class, "sayHello", String.class);
hints.reflection().registerMethod(method, ExecutableMode.INVOKE);
// Register resources
hints.resources().registerPattern("my-resource.txt");
// Register serialization
hints.serialization().registerType(MySerializableClass.class);
// Register proxy
hints.proxies().registerJdkProxy(MyInterface.class);
}
}
然后,您可以使用@ImportRuntimeHints在任何@Configuration类(例如,您的@SpringBootApplication带注释的应用程序类)来激活这些提示。
如果您有需要绑定的类(主要是在序列化或反序列化 JSON 时需要的),您可以使用@RegisterReflectionForBinding在任何豆子上。
大多数提示都是自动推断的,例如,当接受或返回来自@RestController方法。
但是,当您使用WebClient或RestTemplate直接,您可能需要使用@RegisterReflectionForBinding.
4.4.1. 测试自定义提示
这RuntimeHintsPredicatesAPI 可用于测试您的提示。
该 API 提供了构建Predicate可用于测试RuntimeHints实例。
如果您使用的是 AssertJ,您的测试将如下所示:
class MyRuntimeHintsTests {
@Test
void shouldRegisterHints() {
RuntimeHints hints = new RuntimeHints();
new MyRuntimeHints().registerHints(hints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.resource().forResource("my-resource.txt")).accepts(hints);
}
}
4.5. 已知限制
GraalVM 本机映像是一项不断发展的技术,并非所有库都提供支持。 GraalVM 社区通过为尚未发布自己的项目提供可访问性元数据来提供帮助。 Spring 本身不包含第 3 方库的提示,而是依赖于可访问性元数据项目。
如果您在为 Spring Boot 应用程序生成原生镜像时遇到问题,请查看 Spring Boot wiki 的 Spring Boot with GraalVM 页面。 您还可以向 GitHub 上的 spring-aot-smoke-tests 项目贡献问题,该项目用于确认常见应用程序类型是否按预期工作。
如果您发现库不适用于 GraalVM,请在可访问性元数据项目上提出问题。
5. 接下来要读什么
如果您想详细了解我们的构建插件提供的提前处理,请参阅 Maven 和 Gradle 插件文档。
要了解有关用于执行处理的 API 的更多信息,请浏览org.springframework.aot.generate和org.springframework.beans.factory.aotSpring Framework 源代码的包。
有关 Spring 和 GraalVM 的已知限制,请参阅 Spring Boot wiki。
下一节将继续介绍 Spring Boot CLI。