GraalVM 原生映像支持
1. 引入GraalVM原生映像
GraalVM 原生镜像提供了一种部署和运行 Java 应用程序的新方式。 与 Java 虚拟机相比,原生镜像可以以更小的内存占用和更快的启动速度运行。
它们非常适合使用容器镜像部署的应用,尤其当与“功能即服务”(FaaS)平台结合时更具吸引力。
与为JVM编写的传统应用程序不同,GraalVM原生映像应用需要提前处理才能创建可执行文件。 这种提前处理涉及从应用代码的主要入口点静态分析。
GraalVM 原生镜像是一个完整的、针对特定平台的可执行文件。 运行原生镜像不需要自带 Java 虚拟机。
| 如果你只是想开始并尝试 GraalVM,可以跳到“开发你的第一个 GraalVM 原生应用”部分,稍后再回来查看。 |
1.1. 与JVM部署的主要区别
GraalVM 原生镜像是提前生成的,这意味着原生和基于 JVM 的应用程序之间存在一些关键区别。 主要区别如下:
-
对你的应用进行静态分析是在构建时从
主要入口。 -
创建原生镜像时无法访问的代码会被删除,且不会成为可执行文件的一部分。
-
GraalVM 不会直接感知你代码中的动态元素,必须被告知关于反射、资源、序列化和动态代理的相关信息。
-
应用类路径在构建时是固定的,不能更改。
-
没有懒惰类加载,所有可执行文件里发布的内容都会在启动时加载到内存中。
-
Java应用的某些方面存在一些未被完全支持的限制。
除了这些差异外,Spring还使用一种称为Spring提前处理的工艺,这对此施加了更多限制。 请务必至少阅读下一节的开头,了解这些信息。
| GraalVM 参考文档中的原生映像兼容性指南部分提供了关于 GraalVM 限制的更多细节。 |
1.2. 理解春季提前处理
典型的 Spring Boot 应用程序非常动态,配置是在运行时进行的。 事实上,Spring Boot 自动配置的概念很大程度上依赖于对运行时状态的反应,以便正确配置。
虽然可以告诉GraalVM这些应用的动态方面,但这样做会抵消静态分析的大部分优势。 因此,使用 Spring Boot 创建原生镜像时,假设世界是封闭的,应用程序的动态部分受到限制。
封闭世界假设意味着,除了GraalVM本身造成的限制外,还存在以下限制:
-
你应用中定义的豆子在运行时不能更改,意味着:
-
Spring
@Profile注释和配置文件特定配置存在局限性。 -
不支持创建豆子后发生变化的属性(例如,
@ConditionalOnProperty和。使性质)。
-
当这些限制生效时,Spring 可以在构建时进行提前处理,生成 GraalVM 可用的额外资产。 经过 Spring AOT 处理的应用程序通常会生成:
-
Java 源代码
-
字节码(用于动态代理等)
-
GraalVM JSON hint 文件
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 Framework 使用两种不同的概念来管理豆子。 有豆子实例,指的是被创建并可以注入其他豆子的实例。 还有Beans定义,用于定义豆子的属性及其实例应如何创建。
如果我们取一个典型值@Configuration类:
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
豆子定义是通过解析@Configuration类并寻找@Bean方法。
在上述例子中,我们定义了一个豆子定义对于一个名为我的豆子.
我们还在创造一个豆子定义对于MyConfiguration课程本身。
当我的豆子实例是必需的,Spring 知道它必须调用我的豆()方法并使用结果。
在JVM上运行时,@Configuration类解析发生在应用开始时,@Bean方法通过反射被调用。
在创建原生图像时,Spring 的运作方式不同。
而不是解析@Configuration在运行时生成类和生成 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;
}
}
| 具体生成的代码可能会根据你豆子定义的性质而有所不同。 |
你可以看到,生成的代码创建了与@Configuration但以 GraalVM 能够直接理解的方式。
豆子对myConfiguration豆子,还有一个我的豆子.
当我的豆子需要实例,a豆实例提供商被叫去。
该提供商将调用我的豆()方法myConfiguration豆。
| 在春季AOT处理期间,你的申请会被启动,直到豆定义可用为止。 豆子实例不会在AOT处理阶段创建。 |
Spring AOT 会为你所有的 bean 定义生成类似的代码。
当需要豆后处理时(例如,调用)@Autowired方法)。
一应用上下文初始化器还将生成 ,Spring Boot 将用它初始化应用上下文当AOT处理后的应用程序实际运行时。
虽然AOT生成的源代码可能冗长,但相当易读,在调试应用程序时非常有用。
生成的源文件可在以下文件中找到目标/春季进攻/主线/来源当使用 Maven 和构建/生成/进攻的来源与Gradle合作。 |
1.2.2. 提示文件生成
除了生成源文件外,Spring AOT 引擎还会生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,描述 GraalVM 如何处理它通过直接检查代码无法理解的内容。
例如,你可能在私有方法上使用 Spring 注释。 Spring 需要使用反射来调用私有方法,即使是在 GraalVM 上。 当出现这种情况时,Spring 可以写出一个反射提示,让 GraalVM 知道即使私有方法没有直接调用,它仍然需要在本地镜像中可用。
提示文件由以下生成元-INF/原生图像这些数据会被 GraalVM 自动接收。
生成的提示文件可在目标/春季的攻击/主/资源当使用 Maven 和构建/生成/进攻资源与Gradle合作。 |
2. 开发您的第一个GraalVM原生应用
现在我们对 GraalVM 原生映像和 Spring 预置引擎的工作原理有了很好的了解,可以看看如何创建应用程序了。
构建 Spring Boot 原生映像应用主要有两种方式:
-
利用 Spring Boot 支持云原生构建包生成一个包含原生可执行文件的轻量级容器。
-
使用 GraalVM 原生构建工具生成原生可执行文件。
启动新的原生 Spring Boot 项目最简单的方法是进入 start.spring.io,添加“GraalVM Native Support”依赖,然后生成该项目。
包含的HELP.md文件会提供入门提示。 |
2.1. 示例应用
我们需要一个示例应用程序,可以用来创建我们的原生镜像。 就我们而言,“getting-started.html”部分介绍的简单“Hello World!”网页应用就足够了。
总结一下,我们的主要应用代码如下:
@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:最新.
它占地面积小,攻击面也更小,但你也可以使用Paketobuildpacks/builder-jammy-base:最新或Paketobuildpacks/builder-jammy-full:最新以便在需要时在镜像中提供更多工具。 |
2.2.1. 系统需求
应该安装Docker。详情请参见Get Docker。如果你用的是Linux,可以设置成允许非root用户访问。
在macOS上,建议至少将分配给Docker的内存增加到8GB,并且可能还会增加更多CPU。
更多细节请参见Stack Overflow的回答。
在 Microsoft Windows 上,确保启用 Docker WSL 2 后端以提升性能。 |
2.2.2. 使用 Maven
要用 Maven 构建原生图片容器,你应该确保你的pom.xml文件使用Spring靴启动父以及org.graalvm.buildtools:native-maven-plugin.
你应该有<家长>该部分看起来像这样:
<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靴启动父宣告本地配置配置文件,配置需要运行的执行内容以创建原生映像。
你可以用-P命令行标记。
如果你不想用Spring靴启动父你需要配置执行进程进攻的进攻Spring Boot插件中的目标和添加可达性元数据目标来自Native Build Tools插件。 |
要构建镜像,你可以运行Spring Boot:构建映像目标本地个人资料活跃:
$ mvn -Pnative spring-boot:build-image
2.2.3. 使用 Gradle
当应用GraalVM原生映像插件时,Spring Boot Gradle插件会自动配置AOT任务。你应该检查你的Gradle构建是否包含插件该块包括org.graalvm.buildtools.native.
只要org.graalvm.buildtools.native插件被应用,启动构建图像任务生成的是原生镜像,而不是 JVM 镜像。你可以用以下方式运行该任务:
$ gradle bootBuildImage
2.2.4. 运行示例
运行了相应的构建命令后,应该会有一个 Docker 镜像可用。你可以用以下方式启动你的应用程序Docker 运行:
$ 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应用快得多。 |
如果你打开网页浏览器本地主持人:8080你应该会看到以下输出:
Hello World!
要优雅地退出应用,请按ctrl-c.
2.3. 使用原生构建工具构建原生图像
如果你想直接生成原生可执行文件而不使用Docker,可以使用GraalVM原生构建工具。原生构建工具是GraalVM为Maven和Gradle自带的插件。你可以用它们执行各种GraalVM任务,包括生成原生映像。
2.3.1. 前提条件
要用原生构建工具构建原生镜像,你需要在你的机器上安装 GraalVM 发行版。你可以在 Liberica 原生映像包页面手动下载,或者使用像 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 版本:
$ 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
和构建包支持一样,你需要确保你使用的是Spring靴启动父为了继承本地剖面,且org.graalvm.buildtools:native-maven-plugin使用了插件。
与本地配置文件激活时,你可以调用本地:编译目标触发原生图像汇编:
$ mvn -Pnative native:compile
本地镜像可执行文件可在目标目录。
2.3.3. 使用 Gradle
当 Native Build Tools 的 Gradle 插件应用到你的项目时,Spring Boot Gradle 插件会自动触发 Spring AOT 引擎。任务依赖关系是自动配置的,所以你只需运行标准原生编译生成原生图像的任务:
$ 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应用快得多。 |
如果你打开网页浏览器本地主持人: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生成的代码中没有错误,且在应用最终转换为原生镜像时没有遗漏任何内容。
要在JVM上运行Spring Boot应用程序并使用AOT生成的代码,你可以设置spring.aot.enabled系统性质true.
例如:
$ java -Dspring.aot.enabled=true -jar myapplication.jar
你需要确保你测试的jar包含AOT生成的代码。
对于Maven来说,这意味着你应该用-非本地人以激活本地轮廓。
对于Gradle,你需要确保你的构建包含org.graalvm.buildtools.native插件。 |
如果你的申请以spring.aot.enabled属性设置为true那么你对转换成原生图像时能正常工作的信心会更高。
你也可以考虑对正在运行的应用程序进行集成测试。
比如,你可以用SpringWeb客户端调用你的应用程序 REST 端点。
或者你可以考虑用像 Selenium 这样的项目来检查你的应用的 HTML 响应。
3.2. 使用原生构建工具进行测试
GraalVM 原生构建工具支持在原生镜像中运行测试。 当你想深入测试应用内部是否能在GraalVM原生映像中工作时,这会很有帮助。
生成包含测试的原生镜像可能耗时,因此大多数开发者可能更倾向于本地使用 JVM。 不过,它们作为CI流水线的一部分可以非常有用。 例如,你可以选择每天运行一次原生测试。
Spring Framework 包含提前支持测试运行。
所有常见的 Spring 测试功能都能与原生图像测试兼容。
例如,你可以继续使用@SpringBootTest注解。
你也可以用 Spring Boot 测试片只测试应用的具体部分。
Spring Framework 的原生测试支持工作方式如下:
-
通过分析测试以发现
应用上下文这些实例是必需的。 -
每个应用上下文都会进行提前处理并生成资源。
-
创建原生映像,生成的资源由 GraalVM 处理。
-
原生镜像还包含了 JUnit
测试引擎配置为发现测试的列表。 -
启动原生映像,触发引擎运行每次测试并报告结果。
3.2.1. 使用 Maven
要用Maven运行本地测试,确保你的pom.xml文件使用Spring靴启动父.
你应该有<家长>该部分看起来像这样:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.12</version>
</parent>
这Spring靴启动父宣告nativeTest配置运行本地测试所需的执行配置。
你可以用-P命令行标记。
如果你不想用Spring靴启动父你需要配置执行过程测试 AOTSpring Boot 插件中的目标和测试目标来自Native Build Tools插件。 |
要构建镜像并运行测试,请使用测试目标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();
}
哪里嵌 套是:
public class Nested {
private int number;
}
上述示例产生了 的配置性质my.properties.name和my.properties.nested.number. 没有@NestedConfigurationProperty关于嵌 套字段,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
)
| 请在所有情况下使用公开的获取者和设定者,否则属性将无法绑定。 |
4.2. 转换 Spring Boot 可执行 jar
只要 jar 包含 AOT 生成的资产,就可以将 Spring Boot 的可执行文件 jar 转换为原生映像。这有多种用途,包括:
-
你可以保留常规的JVM流水线,把JVM应用转为CI/CD平台上的原生镜像。
-
如
原生图像不支持交叉编译,你可以保留一个作系统中立的部署工件,之后再转换到不同的作系统架构。
你可以用Cloud Native Buildpacks将Spring Boot的可执行文件jar转换成原生镜像,或者使用原生图像该工具随GraalVM一同发布。
| 你的可执行 jar 必须包含 AOT 生成的资源,比如生成的类和 JSON 提示文件。 |
4.2.1. 构建包的使用
Spring Boot 应用程序通常通过 Maven 使用 Cloud Native 构建包(MVN Spring-boot:build-image)或Gradle(gradle bootBuildImage)集成。不过,你也可以使用包将AOT处理的Spring Boot可执行文件jar转换为本地容器镜像。
首先,确保有 Docker 守护进程可用(详情请参见获取 Docker )。如果你用的是 Linux,可以配置成允许非 root 用户访问。
你还需要安装包通过按照 buildpacks.io 上的安装指南作。
假设AOT处理了Spring Boot可执行文件jar,构建为myproject-0.0.1-SNAPSHOT.jar属于目标目录,运行:
$ 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 安装就能以这种方式生成映像。 |
一次包完成后,你可以用以下方式启动应用Docker 运行:
$ docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT
4.2.2. 使用 GraalVM 原生映像
将AOT处理的Spring Boot可执行文件jar转换为原生可执行文件的另一种方法是使用GraalVM原生图像工具。 要实现这一点,你需要在你的机器上安装 GraalVM 发行版。你可以在 Liberica 原生映像包页面手动下载,或者使用像 SDKMAN! 这样的下载管理器。
假设AOT处理了Spring Boot可执行文件jar,构建为myproject-0.0.1-SNAPSHOT.jar属于目标目录,运行:
$ 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里。只有在需要覆盖可达元数据时才会包含它。 |
这原生图像 -cpflag 不接受万用符。你需要确保所有 jar 都被列出(上述命令使用找到和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然后对所提供的 进行适当调用。运行提示实例:
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方法。
但当你与Web客户端,Rest客户端或Rest模板直接来说,你可能需要使用@RegisterReflectionForBinding.
4.4.1. 测试自定义提示
这运行提示谓词API可以用来测试你的提示。
该 API 提供了构建谓语可以用来测试运行提示实例。
如果你用的是 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/在元-INF/原生图像/*/*/目录。
AOT处理过程中生成的提示会写入一个名为META-INF/native-image/{groupId}/{artifactId}/.
将静态提示文件放置在与该位置不冲突的目录中,例如META-INF/native-image/{groupId}/{artifactId}-additional-hints/
4.5. 已知的局限性
GraalVM 原生镜像是一项不断发展的技术,并非所有库都支持。 GraalVM 社区通过为尚未发布的项目提供可达元数据来提供帮助。 Spring本身不包含第三方库的提示,而是依赖可达性元数据项目。
如果您在为 Spring Boot 应用程序生成原生镜像时遇到问题,请查看 Spring Boot 维基中的 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 维基。
下一节将介绍Spring Boot CLI。