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 Bean 组成。在内部,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 定义myConfiguration
bean,一个用于myBean
. 当myBean
instance 是必需的,则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-image
GraalVM 会自动获取它们。
生成的提示文件可以在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.md file 将提供入门提示。 |
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.0.13</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.0.13)
....... . . .
....... . . . (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
为了继承native
profile 和org.graalvm.buildtools:native-maven-plugin
插件。
使用native
profile 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.0.13)
....... . . .
....... . . . (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.0.13</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-image
GraalVM 附带的工具。
可执行 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. 测试自定义提示
这RuntimeHintsPredicates
API 可用于测试您的提示。该 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.aot
Spring Framework 源代码的包。
有关 Spring 和 GraalVM 的已知限制,请参阅 Spring Boot wiki。
下一节将继续介绍 Spring Boot CLI。