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 时更改的属性(例如,和 properties)。
@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 定义是通过解析类并查找方法创建的。
在上面的示例中,我们为名为 .
我们还为类本身创建了一个 API。@Configuration
@Bean
BeanDefinition
myBean
BeanDefinition
MyConfiguration
当需要实例时, Spring 知道它必须调用该方法并使用结果。
在 JVM 上运行时,当应用程序启动时,将使用反射调用方法时进行类解析。myBean
myBean()
@Configuration
@Bean
在创建本机映像时, Spring 以不同的方式运行。
它不是在运行时解析类和生成 bean 定义,而是在构建时进行。
一旦发现 Bean 定义,它们就会被处理并转换为源代码,供 GraalVM 编译器分析。@Configuration
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 定义的性质而异。 |
您可以在上面看到,生成的代码创建了与类等效的 bean 定义,但以 GraalVM 可以理解的直接方式创建。@Configuration
有一个 bean 的 bean 定义,一个 bean 的定义。
当需要一个实例时,将调用 a。
此供应商将在 bean 上调用该方法。myConfiguration
myBean
myBean
BeanInstanceSupplier
myBean()
myConfiguration
在 Spring AOT 处理期间,您的应用程序将启动,直到 bean 定义可用为止。 在 AOT 处理阶段不会创建 Bean 实例。 |
Spring AOT 将为你的所有 bean 定义生成这样的代码。
当需要 bean 后处理时(例如,调用方法),它还将生成代码。
还将生成一个 Spring Boot 在实际运行 AOT 处理的应用程序时使用它来初始化。@Autowired
ApplicationContextInitializer
ApplicationContext
尽管 AOT 生成的源代码可能很冗长,但它非常可读,并且在调试应用程序时可能会有所帮助。
生成的源文件可以在 使用 Maven 时和 Gradle 中找到。target/spring-aot/main/sources build/generated/aotSources |
1.2.2. Hint 文件生成
除了生成源文件外,Spring AOT 引擎还将生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,描述 GraalVM 应如何通过直接检查代码来处理无法理解的事情。
例如,您可能正在私有方法上使用 Spring 注释。 Spring 需要使用反射来调用私有方法,即使在 GraalVM 上也是如此。 当出现此类情况时, Spring 可以编写反射提示,以便 GraalVM 知道,即使私有方法未直接调用,它仍然需要在本机映像中可用。
提示文件是在 GraalVM 自动选取它们的位置下生成的。META-INF/native-image
生成的提示文件可以在 使用 Maven 时和 Gradle 中找到。target/spring-aot/main/resources build/generated/aotResources |
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 守护进程是否按预期可访问。
有关更多详细信息,请查看 Maven 或 Gradle Spring Boot 插件文档。docker run hello-world sudo |
在 macOS 上,建议将分配给 Docker 的内存至少增加到 ,并可能添加更多 CPU。
有关更多详细信息,请参阅此 Stack Overflow 答案。
在 Microsoft Windows 上,请确保启用 Docker WSL 2 后端以获得更好的性能。8GB |
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 插件中的目标和 Native Build Tools 插件中的目标配置执行。spring-boot-starter-parent process-aot add-reachability-metadata |
要构建图像,您可以在配置文件处于活动状态的情况下运行目标:spring-boot:build-image
native
$ mvn -Pnative spring-boot:build-image
2.2.3. 使用 Gradle
Spring Boot Gradle 插件在应用 GraalVM Native Image 插件时自动配置 AOT 任务。
您应该检查您的 Gradle 构建是否包含包含 .plugins
org.graalvm.buildtools.native
只要应用了插件,该任务就会生成本机映像,而不是 JVM 映像。
您可以使用以下方法运行任务:org.graalvm.buildtools.native
bootBuildImage
$ 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. 使用 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 支持一样,您需要确保您正在使用 for inherit the profile 并且使用了插件。spring-boot-starter-parent
native
org.graalvm.buildtools:native-maven-plugin
在配置文件处于活动状态的情况下,您可以调用 goal 来触发编译:native
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 生成的代码。
由于编译阶段可能需要一段时间才能完成,因此有时在 JVM 上运行应用程序,但让它使用 AOT 生成的初始化代码是有用的。
这样做有助于您快速验证 AOT 生成的代码中没有错误,并且在应用程序最终转换为本机映像时没有遗漏任何内容。native-image
要在 JVM 上运行 Spring Boot 应用程序并使其使用 AOT 生成的代码,可以将 system 属性设置为。spring.aot.enabled
true
例如:
$ java -Dspring.aot.enabled=true -jar myapplication.jar
您需要确保正在测试的 jar 包含 AOT 生成的代码。
对于 Maven,这意味着您应该使用 构建 来激活配置文件。
对于 Gradle,您需要确保您的构建包含插件。-Pnative native org.graalvm.buildtools.native |
如果应用程序开始时将属性设置为 ,则您更确信它在转换为本机映像时将正常工作。spring.aot.enabled
true
您还可以考虑针对正在运行的应用程序运行集成测试。
例如,您可以使用 Spring 调用应用程序 REST 端点。
或者,您可以考虑使用像 Selenium 这样的项目来检查应用程序的 HTML 响应。WebClient
3.2. 使用原生构建工具进行测试
GraalVM Native Build Tools 能够在原生映像中运行测试。 当您想深入测试应用程序的内部结构是否在 GraalVM 原生映像中工作时,这可能很有帮助。
生成包含要运行的测试的本机映像可能是一项耗时的操作,因此大多数开发人员可能更愿意在本地使用 JVM。 但是,它们作为 CI 管道的一部分非常有用。 例如,您可以选择每天运行一次本机测试。
Spring Framework 包括对运行测试的预先支持。
所有常用的 Spring 测试功能都适用于本机映像测试。
例如,您可以继续使用 Annotation。
您还可以使用 Spring Boot 测试切片仅测试应用程序的特定部分。@SpringBootTest
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 插件中的目标和 Native Build Tools 插件中的目标配置执行。spring-boot-starter-parent process-test-aot test |
要构建映像并运行测试,请在配置文件处于活动状态的情况下使用目标:test
nativeTest
$ mvn -PnativeTest test
4. 高级原生映像主题
4.1. 嵌套配置属性
Spring 预配置引擎会自动为 configuration 属性创建反射提示。
但是,不是内部类的嵌套配置属性必须用 Comments ,否则它们不会被检测到并且不可绑定。@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 平台上的本机映像。
-
由于不支持交叉编译,因此您可以保留一个 OS 中立的部署工件,稍后将其转换为不同的 OS 架构。
native-image
您可以使用 Cloud Native Buildpacks 或使用 GraalVM 附带的工具将 Spring Boot 可执行 jar 转换为本机映像。native-image
您的可执行 jar 必须包含 AOT 生成的资产,例如生成的类和 JSON 提示文件。 |
4.2.1. 使用 Buildpack
Spring Boot 应用程序通常通过 Maven () 或 Gradle () 集成使用 Cloud Native Buildpacks。
但是,您也可以使用 pack
将 AOT 处理的 Spring Boot 可执行 jar 转换为本机容器映像。mvn spring-boot:build-image
gradle bootBuildImage
首先,确保 Docker 守护程序可用(有关更多详细信息,请参阅获取 Docker)。如果您使用的是 Linux,请将其配置为允许非 root 用户。
您还需要按照 buildpacks.io 上的安装指南进行安装。pack
假设 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 转换为本机可执行文件的另一种选择是使用 GraalVM 工具。
为此,您的机器上需要一个 GraalVM 发行版。
您可以在 Liberica Native Image Kit 页面上手动下载它,也可以使用 SDKMAN! 等下载管理器。native-image
假设 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 进行调整。 |
可能没有打包在您的 jar 中。
仅当需要可访问性元数据覆盖时,才会包含它。@META-INF/native-image/argfile |
该标志不接受通配符。
您需要确保列出所有 jar(上面的命令使用 and 来执行此操作)。native-image -cp 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 输出目录。
您可以手动检查这些文件,也可以将它们用作本机映像构建过程的输入。
要将它们用作输入,请将它们复制到目录中。
下次构建原生镜像时,GraalVM 将考虑这些文件。src/main/resources/META-INF/native-image/
可以在本机图像跟踪代理上设置更高级的选项,例如,按调用方类过滤记录的提示等。 如需进一步阅读,请参阅官方文档。
4.4. 自定义提示
如果您需要为反射、资源、序列化、代理使用等提供自己的提示,则可以使用 API。
创建一个实现接口的类,然后对提供的实例进行适当的调用: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 时需要),则可以在任何 bean 上使用 @RegisterReflectionForBinding
。
大多数提示都是自动推断的,例如,当接受或返回来自方法的数据时。
但是,当您使用 OR 直接使用时,您可能需要使用 .@RestController
WebClient
RestTemplate
@RegisterReflectionForBinding
4.4.1. 测试自定义 Hint
API 可用于测试您的提示。
API 提供了构建可用于测试实例的方法。RuntimeHintsPredicates
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 本身不包含 3rd 方库的提示,而是依赖于可访问性元数据项目。
如果您在为 Spring Boot 应用程序生成原生映像时遇到问题,请查看 Spring Boot wiki 的 Spring Boot with GraalVM 页面。 您还可以将问题贡献给 GitHub 上的 spring-aot-smoke-tests 项目,该项目用于确认常见应用程序类型是否按预期工作。
如果您发现某个库无法与 GraalVM 一起使用,请在可访问性元数据项目上提出问题。
5. 接下来要读什么
如果您想详细了解我们的构建插件提供的预先处理,请参阅 Maven 和 Gradle 插件文档。
要了解有关用于执行处理的 API 的更多信息,请浏览 Spring Framework 源的 和 包。org.springframework.aot.generate
org.springframework.beans.factory.aot
有关 Spring 和 GraalVM 的已知限制,请参阅 Spring Boot Wiki。
下一节将继续介绍 Spring Boot CLI。