GraalVM Native Image 支持
1. GraalVM 原生镜像简介
GraalVM 原生映像提供了一种部署和运行 Java 应用程序的新方法。 与 Java 虚拟机相比,本机映像可以以更小的内存占用和更快的启动时间运行。
它们非常适合使用容器映像部署的应用程序,在与“功能即服务”(FaaS) 平台结合使用时尤其有趣。
与为 JVM 编写的传统应用程序不同,GraalVM Native Image 应用程序需要提前处理才能创建可执行文件。 这种预先处理涉及从应用程序代码的主入口点静态分析应用程序代码。
GraalVM Native Image 是一个完整的、特定于平台的可执行文件。 您无需提供 Java 虚拟机即可运行本机映像。
如果您只想开始并试用 GraalVM,可以跳到“开发您的第一个 GraalVM 原生应用”部分,稍后再返回此部分。 |
1.1. 与 JVM 部署的主要区别
GraalVM Native Images 是提前生成的,这意味着原生应用和基于 JVM 的应用之间存在一些关键差异。 主要区别在于:
-
应用程序的静态分析在构建时从
main
入口点。 -
创建本机映像时无法访问的代码将被删除,并且不会成为可执行文件的一部分。
-
GraalVM 无法直接了解代码的动态元素,必须了解反射、资源、序列化和动态代理。
-
应用程序 Classpath 在构建时是固定的,无法更改。
-
没有延迟类加载,可执行文件中附带的所有内容都将在启动时加载到内存中。
-
Java 应用程序的某些方面存在一些不完全支持的限制。
除了这些差异之外, Spring 还使用了一个称为 Spring Ahead-of-Time processing 的过程,这施加了进一步的限制。 请务必至少阅读下一部分的开头部分以了解这些内容。
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 提示文件
META-INF/native-image/{groupId}/{artifactId}/
:-
资源提示 (
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
方法使用 Reflection 调用。
在创建本机映像时, 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. Hint 文件生成
除了生成源文件外,Spring AOT 引擎还将生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,描述 GraalVM 应如何通过直接检查代码来处理无法理解的事情。
例如,您可能正在私有方法上使用 Spring 注解。 Spring 需要使用反射来调用私有方法,即使在 GraalVM 上也是如此。 当出现此类情况时, Spring 可以编写反射提示,以便 GraalVM 知道,即使私有方法未直接调用,它仍然需要在本机映像中可用。
提示文件在META-INF/native-image
它们被 GraalVM 自动获取。
生成的 Hint 文件可以在target/spring-aot/main/resources 使用 Maven 和build/generated/aotResources 与 Gradle 一起使用。 |
2. 开发您的第一个 GraalVM 原生应用程序
现在我们已经很好地了解了 GraalVM Native Images 以及 Spring 预验证引擎的工作原理,我们可以看看如何创建应用程序。
构建 Spring Boot 本机映像应用程序有两种主要方法:
-
使用 Spring Boot 对 Cloud Native Buildpacks 的支持来生成包含本机可执行文件的轻量级容器。
-
使用 GraalVM Native Build Tools 生成原生可执行文件。
启动新的原生 Spring Boot 项目的最简单方法是转到 start.spring.io,添加“GraalVM Native Support”依赖项并生成项目。
包含HELP.md 文件将提供入门提示。 |
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. 使用 buildpack 构建原生镜像
Spring Boot 包括对直接用于 Maven 和 Gradle 的本机映像的 buildpack 支持。 这意味着您只需键入一个命令,即可快速将合理的映像获取到本地运行的 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
file 使用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.2.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 goal 和add-reachability-metadata 目标。 |
要构建镜像,您可以运行spring-boot:build-image
goal 替换为native
配置文件处于活动状态:
$ mvn -Pnative spring-boot:build-image
2.2.3. 使用 Gradle
Spring Boot Gradle 插件在应用 GraalVM Native Image 插件时自动配置 AOT 任务。
您应该检查您的 Gradle 构建是否包含plugins
块,其中包括org.graalvm.buildtools.native
.
只要org.graalvm.buildtools.native
plugin 时,使用bootBuildImage
task 将生成本机映像而不是 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.2.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. 使用 Native Build Tools 构建 Native Image
如果您想在不使用 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 上,请按照这些说明安装 GraalVM 或 Liberica Native Image Kit 版本 22.3、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
plugin 的 intent 值。
使用native
配置文件处于活动状态,您可以调用native:compile
要触发的目标native-image
汇编:
$ mvn -Pnative native:compile
本机映像可执行文件可以在target
目录。
2.3.3. 使用 Gradle
当 Native Build Tools Gradle 插件应用到您的项目时,Spring Boot Gradle 插件将自动触发 Spring AOT 引擎。
任务依赖关系是自动配置的,因此您只需运行标准的nativeCompile
task 生成原生镜像:
$ gradle nativeCompile
本机映像可执行文件可以在build/native/nativeCompile
目录。
2.3.4. 运行示例
此时,您的应用程序应该可以正常工作。现在,您可以通过直接运行应用程序来启动应用程序:
$ target/myproject
$ build/native/nativeCompile/myproject
您应该会看到类似于以下内容的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.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
system 属性设置为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 Native Build Tools 能够在原生映像中运行测试。 当您想深入测试应用程序的内部结构是否在 GraalVM 原生映像中工作时,这可能很有帮助。
生成包含要运行的测试的本机映像可能是一项耗时的作,因此大多数开发人员可能更愿意在本地使用 JVM。 但是,它们作为 CI 管道的一部分非常有用。 例如,您可以选择每天运行一次本机测试。
Spring Framework 包括对运行测试的预先支持。
所有常用的 Spring 测试功能都适用于本机映像测试。
例如,您可以继续使用@SpringBootTest
注解。
您还可以使用 Spring Boot 测试切片仅测试应用程序的特定部分。
Spring Framework 的原生测试支持以以下方式工作:
-
分析测试以发现任何
ApplicationContext
实例。 -
将提前处理应用于每个应用程序上下文,并生成资产。
-
将创建一个原生镜像,生成的资产由 GraalVM 处理。
-
本机映像还包括 JUnit
TestEngine
配置了已发现测试的列表。 -
本机映像启动,触发将运行每个测试并报告结果的引擎。
3.2.1. 使用 Maven
要使用 Maven 运行原生测试,请确保您的pom.xml
file 使用spring-boot-starter-parent
.
您应该有一个<parent>
部分,如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.12</version>
</parent>
这spring-boot-starter-parent
声明nativeTest
配置文件,用于配置运行本机测试所需的执行。
您可以使用-P
标志。
如果您不想使用spring-boot-starter-parent 您需要为process-test-aot goal 和test 目标。 |
要构建镜像并运行测试,请使用test
goal 替换为nativeTest
配置文件处于活动状态:
$ mvn -PnativeTest test
4. 高级原生映像主题
4.1. 嵌套配置属性
Spring 预配置引擎会自动为 configuration 属性创建反射提示。
但是,不是内部类的嵌套配置属性必须使用@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
)
请在所有情况下使用 public getter 和 setter,否则属性将不可绑定。 |
4.2. 转换 Spring Boot 可执行 Jar
只要 Spring Boot 可执行 jar 包含 AOT 生成的资产,就可以将 Spring Boot 可执行 jar 转换为本机映像。 这可能很有用,原因有很多,包括:
-
您可以保留常规的 JVM 管道,并将 JVM 应用程序转换为 CI/CD 平台上的本机映像。
-
如
native-image
不支持交叉编译,您可以保留一个 OS 中立的部署工件,稍后将其转换为不同的 OS 架构。
您可以使用 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 native-image
将 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 flag 不接受通配符。
您需要确保列出所有 jar(上面的命令使用find 和tr 执行此作)。 |
4.3. 使用 Tracing Agent
GraalVM 原生图像跟踪代理允许您拦截 JVM 上的反射、资源或代理使用情况,以生成相关提示。 Spring 应该自动生成大部分这些提示,但是可以使用跟踪代理来快速识别缺少的条目。
使用代理为本机映像生成提示时,有几种方法:
-
直接启动应用程序并执行它。
-
运行应用程序测试以执行应用程序。
第一个选项对于当 Spring 无法识别库或模式时识别缺少的提示很有趣。
第二个选项听起来更吸引可重复的设置,但默认情况下,生成的 Importing 将包括测试基础结构所需的任何内容。 当应用程序真正运行时,其中一些是不必要的。 为了解决这个问题,代理支持一个访问过滤器文件,这将导致某些数据被排除在生成的输出之外。
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
.
在应用程序关闭时,本机映像跟踪代理会将提示文件写入给定的 config 输出目录。
您可以手动检查这些文件,也可以将它们用作本机映像构建过程的输入。
要将它们用作输入,请将它们复制到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
annotated application 类)来激活这些提示。
如果您有需要绑定的类(主要是在序列化或反序列化 JSON 时需要),则可以使用@RegisterReflectionForBinding
在任何 bean 上。
大多数提示都是自动推断的,例如,当接受或返回来自@RestController
方法。
但是,当您使用WebClient
,RestClient
或RestTemplate
直接,您可能需要使用@RegisterReflectionForBinding
.
4.4.1. 测试自定义 Hint
这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.4.2. 静态提供提示
如果您愿意,可以在一个或多个 GraalVM JSON 提示文件中静态提供自定义提示。
此类文件应放在src/main/resources/
在META-INF/native-image/*/*/
目录。
AOT 处理过程中生成的提示将写入名为META-INF/native-image/{groupId}/{artifactId}/
.
将静态提示文件放在不与此位置冲突的目录中,例如META-INF/native-image/{groupId}/{artifactId}-additional-hints/
4.5. 已知限制
GraalVM 原生映像是一项不断发展的技术,并非所有库都提供支持。 GraalVM 社区通过为尚未发布自己的项目提供可访问性元数据来提供帮助。 Spring 本身不包含 3rd 方库的提示,而是依赖于可访问性元数据项目。
如果您在为 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。