GraalVM Native Image学习记笔
目录
3.2 使用Native Build Tools(本机构建工具)进行测试
1、GraalVM Native Images介绍
1.1 GraalVM Native Images简介
- GraalVM提供一种全新的方式让你发布及运行你的java应用,与传统的JVM相比,具备更小的内存占用合更快的启动速度。
- 非常适合用容器来部署应用,适合Function as a service(FaaS)应用场景。
- GrralVM使用AOT技术,在程序运行前将源代码编译为二进制的可执行的静态文件,并确定应用程序入口。
- GraalVM本机映像是一个完整的、特定于平台的可执行文件。不需要为了运行本机映像而提供Java虚拟机。
1.2 和JVM部署的主要区别
- 应用程序的静态分析是在构建时从主入口点执行的。
- 创建本机映像时无法访问的代码将被删除,并且不会成为可执行文件的一部分。
- GraalVM不直接知道代码中的动态元素,必须了解反射、资源、序列化和动态代理。
- 应用程序类路径在构建时是固定的,不能更改。
- 没有延迟类加载,可执行文件中提供的所有内容都将在启动时加载到内存中。
- Java应用程序的某些方面存在一些不完全支持的限制。
GraalVM参考文档的Native Image Compatibility Guide部分提供了有关GraalVM限制的更多详细信息。
Native Image Compatibility Guide
1.3 了解Spring AOT原理
典型的Spring Boot应用程序是动态的,配置是在运行时执行的。事实上,Spring Boot自动配置的概念在很大程度上取决于对运行时状态的反应,以便正确配置。尽管可以告诉GraalVM应用程序的这些动态方面,但这样做会抵消静态分析的大部分好处。因此,当使用Spring Boot创建本机映像时,假设是一个封闭的世界,并且应用程序的动态方面受到限制。
SpringBoot本地编译限制:
- 类路径是固定的,并在构建时完全定义
- 应用程序中定义的bean在运行时无法更改,这意味着:
不支持Spring@Profile注释和特定于配置文件的配置
不支持创建bean时更改的Properties(例如@ConditionalOnProperty和.enable Properties)
当这些限制是明确的,GraalV在构建Spring时提前处理, 通过AOT处理通常会生成:
- java源代码
- 字节码(用于动态代理等)
- GraalVM JSON提示文件:
- 资源提示(Resource-config.json)
- 反射提示(reflect-config.json)
- 序列化提示(Serialization-config.json)
- Java代理提示(Proxy-config.json)
- JNI提示(JNI-config.json)
1.3.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方法来创建的。在上面的例子中,我们为一个名为myBean的单例bean定义了BeanDefinition。我们还在为MyConfiguration类本身创建一个BeanDefinition。
当需要myBean实例时,Spring知道它必须调用myBean()方法并使用结果。当在JVM上运行时,当应用程序启动并且使用反射调用@Bean方法时,就会进行@Configuration类解析。
在创建本地映像时,Spring以不同的方式进行操作。它不是在运行时解析@Configuration类并生成bean定义,而是在构建时进行。一旦发现了bean定义,就会对其进行处理并将其转换为源代码,GraalVM编译器可以对其进行分析。
Spring AOT进程会将上面的配置类转换为如下代码:
import org.springframework.beans.factory.aot.BeanInstanceSupplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
/**
* 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;
}
}
Note1:
据bean定义的性质,生成的确切代码可能会有所不同。
可以在上面看到,生成的代码创建了与@Configuration类等效的bean定义,但这是GraalVM可以理解的直接方式。
myConfigurationbean有一个bean定义,myBean也有一个定义。当需要myBean实例时,会调用BeanInstanceSupplier。此供应商将调用myConfigurationbean上的myBean()方法。
Note2:
在Spring AOT处理过程中,应用程序会启动,并会指向那些可用的ben。Bean实例不是在AOT处理阶段创建的。
Spring AOT将为所有bean定义生成这样的代码。它还将在需要bean后处理时生成代码(例如,调用@Autowired方法)。还将生成ApplicationContextInitializer,当AOT处理的应用程序实际运行时,Spring Boot将使用它来初始化ApplicationContext。
尽管AOT生成的源代码可能很冗长,但它可读性很强,在调试应用程序时也很有帮助。当使用Maven和带有Gradle的build/regenerated/aotSources时,可以在target/spring aot/main/sources中找到生成的源文件。
1.3.2. 提示文件生成
除了生成源文件外,Spring AOT引擎还将生成GraalVM使用的提示文件。提示文件包含JSON数据,这些数据描述GraalVM应该如何处理它无法通过直接检查代码来理解的事情。
例如,可能正在私有方法上使用Spring注释。Spring将需要使用反射来调用私有方法,即使在GraalVM上也是如此。当出现这种情况时,Spring可以编写一个反射提示,以便GraalVM知道,即使不直接调用私有方法,它仍然需要在本机映像中可用。
提示文件是在META-INF/本地映像下生成的,GraalVM会自动拾取这些文件。
当使用Maven和带有Gradle的build/regenerated/aotResources时,可以在target/spring aot/main/resources中找到生成的提示文件。
1.3.3. 代理类生成
Spring有时需要生成代理类来增强编写的具有附加功能的代码。为此,它使用了cglib库,该库直接生成字节码。
当应用程序在JVM上运行时,代理类会随着应用程序的运行而动态生成。创建本机映像时,需要在构建时创建这些代理,以便GraalVM可以包含这些代理。
与源代码生成不同,生成的字节码在调试应用程序时并没有特别的帮助。然而,如果需要使用javap等工具检查.class文件的内容,可以在Maven的target/spring aot/main/classes和Gradle的build/regenerated/aotClasses中找到它们。
2、开始你的第一个GraalVM Native 应用
现在我们已经很好地概述了GraalVM Native Images以及Spring提前引擎的工作原理,我们可以看看如何创建应用程序。
构建Spring Boot本机映像应用程序主要有两种方法:
- 使用Spring Boot对Cloud Native Buildpacks的支持来生成包含本机可执行文件的轻量级容器。
- 使用GraalVM本机构建工具生成本机可执行文件。
Note1:
启动新的本机Spring Boot项目的最简单方法是转到start.Spring.io,添加“GraalVM native Support”依赖项并生成项目。包含的HELP.md文件将提供入门提示。
2.1 应用示例
我们需要一个可以用来创建本地映像的示例应用程序。就我们的目的而言,“入门.html”部分介绍的简单的“Hello World!”web应用程序就足够了。
概括一下,我们的主要应用程序代码如下所:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@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 Buildpacks构建
Spring Boot支持对Maven和Gradle的本地imaeg的直接构建。这意味着只需键入一个命令,就可以在本地运行的Docker守护进程中快速获得一个合理的image。生成的image不包含JVM,而是静态编译本机image。这会导致image变小。
Note1:
用于image的构建器是:paketobuildpacks/builder:tiny。它具有体积小,轻量级特点,但如果需要,也可以使用paketobuildpacks/builder:base或paketobbuildpacks/builder:full在imags中提供更多工具。
2.2.1 环境要求
应该安装docker。有关更多详细信息,请参阅Get-Docker。如果在Linux上,请将其配置为允许非root用户。
Note1:
可以运行docker-run-helloworld(不带sudo)来检查docker守护进程是否可以按预期访问。查看Maven或Gradle Spring Boot插件文档以了解更多详细信息。
在macOS上,建议将分配给Docker的内存增加到至少8GB,并可能增加更多CPU。有关更多详细信息,请参阅此堆栈溢出答案。在Microsoft Windows上,请确保启用Docker WSL 2后端以获得更好的性能。
2.2.2 Maven构建
要使用Maven构建本地映像容器,应该确保pom.xml文件使用spring-boot-starter父文件和org.graalvm.buildtools:native Maven插件。应该有一个部分,如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
</parent>
此外,应该在<build><plugins>部分中有这样的内容:
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
spring-boot-starter父级声明一个本机配置文件,该配置文件配置创建本机映像所需运行的执行。你可以使用命令行上的-P标志激活配置文件。
如果你不想使用spring-boot starter父级,你需要从spring-boot的插件中为进程aot目标和从Native Build Tools插件中为添加可达性元数据目标配置执行。
要构建映像,可以在本机配置文件处于活动状态的情况下运行spring-boot:build映像目标:
$ mvn -Pnative spring-boot:build-image
2.2.3 Gradle构建
当应用GraalVM Native Image插件时,Spring Boot Gradle插件会自动配置AOT任务。应该检查的Gradle构建是否包含一个插件块,其中包含 org.graalvm.buildtools.native。
只要应用了org.graalvm.buildtools.native插件,bootBuildImage任务就会生成一个本地映像,而不是JVM映像。可以使用以下方法运行任务:
$ gradle bootBuildImage
2.2.4 Run Example
一旦运行了相应的构建命令,Docker镜像就应该可用了。可以使用docker run启动应用程序:
$ docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT
___ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.5) ....... . . . ....... . . . (log output here) ....... . . . ........ Started MyApplication in 0.08 seconds (process running for 0.095)
启动时间因机器而异,但应该比JVM上运行的Spring Boot应用程序快得多。
如果打开一个web浏览器到localhost:8080,应该会看到以下输出:
你好,世界!
要正常退出应用程序,请按ctrl-c。
2.3 Native Build Tools构建
如果想在不使用Docker的情况下直接生成本机可执行文件,可以使用GraalVM本机构建工具。Native Build Tools是GraalVM为Maven和Gradle提供的插件。可以使用它们来执行各种GraalVM任务,包括生成本地映像。
2.3.1 环境要求
- Linux and macOS
要使用本机构建工具构建本机映像,需要在机器上安装GraalVM分发版。可以在Liberica Native Image Kit页面上手动下载,也可以使用SDKMAN!这样的下载管理器!。
$ 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
在Windows上,按照以下说明安装版本22.3中的GraalVM或Liberica Native Image Kit、Visual Studio构建工具和Windows SDK。由于与Windows相关的命令行最大长度,请确保使用x64 Native Tools命令提示符而不是常规的Windows命令行来运行Maven或Gradle插件。
2.3.2 Maven构建
与buildpack支持一样,需要确保使用spring-boot-starter-parent来继承本机profile文件,并且使用org.graalvm.buildtools:native-maven插件。
在激活本机profile文件的情况下,可以调用native:compile目标来触发本机映像编译:
$ mvn -Pnative native:compile
可以在目标目录中找到本机映像可执行文件。
2.3.3 Gradle构建
当Native Build Tools Gradle插件应用于的项目时,Spring Boot Gradle插件将自动触发Spring AOT引擎。任务依赖项是自动配置的,因此只需运行标准的nativeCompile任务即可生成本地映像:
$ gradle nativeCompile
本机映像可执行文件可以在build/nature/natureCompile目录中找到。
2.3.4 Run Example
此时,的应用程序应该可以工作了。现在,可以通过直接运行应用程序来启动它:
Maven
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.5) ....... . . . ....... . . . (log output here) ....... . . . ........ Started MyApplication in 0.08 seconds (process running for 0.095)
启动时间因机器而异,但应该比JVM上运行的Spring Boot应用程序快得多。
如果打开一个web浏览器到localhost:8080,应该会看到以下输出:
你好,世界!
要正常退出应用程序,请按ctrl-c。
3、测试 GraalVM Native Images
在编写本机images应用程序时,我们建议尽可能继续使用JVM来开发大部分单元和集成测试。这将有助于降低开发人员的构建时间,并允许使用现有的IDE集成。通过对JVM的广泛测试覆盖,可以将本机images测试集中在可能不同的领域。
对于本机images测试,通常希望确保以下方面有效:
- Spring AOT引擎能够处理的应用程序,并且它将以AOT处理模式运行。
- GraalVM有足够的提示来确保可以生成有效的本机images
3.1 使用JVM进行AOT测试
当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来说,这意味着应该使用-Pnative进行构建以激活本机概要文件。对于Gradle,需要确保的构建包含org.graalvm.buildtools.native插件。
如果你的应用程序在启动时将spring.aot.enabled属性设置为true,那么你就更有信心在转换为本机映像时它会工作。
你还可以考虑针对正在运行的应用程序运行集成测试。例如,可以使用SpringWebClient来调用应用程序REST端点。或者可以考虑使用Selenium这样的项目来检查应用程序的HTML响应。
3.2 使用Native Build Tools(本机构建工具)进行测试
GraalVM本机构建工具包括在本机映像中运行测试的功能。当你想深入测试应用程序的内部是否在GraalVM本机映像中工作时,这可能会很有帮助。
生成包含要运行的测试的本机images可能是一项耗时的操作,因此大多数开发人员可能更喜欢在本地使用JVM。然而,作为CI管道的一部分,它们可能非常有用。例如,你可以选择每天运行一次本机测试。
Spring Framework包括对运行测试的AOT支持。所有常见的Spring测试功能都可以使用本机iamges测试。例如,你可以继续使用@SpringBootTest注释。你也可以使用 Spring Boot test slices来只测试应用程序的特定部分。
Spring Framework的本机测试支持以以下方式工作:
- 对测试进行分析,以发现所需的任何ApplicationContext实例。
- 将提前处理应用于这些应用程序上下文中的每一个,并生成资产。
- 创建一个本地images,生成的资源由GraalVM处理。
- 本机images还包括JUnit测试引擎,该引擎配置有已发现测试的列表。
- 启动本机images,触发引擎,该引擎将运行每个测试并报告结果。
3.2.1 Using Maven
要使用Maven运行本机测试,请确保pom.xml文件使用spring-boot-starter父级,你应该有一个部分,如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
</parent>
spring-boot-starter父级声明了一个nativeTest配置文件,该配置文件配置运行本机测试所需的执行。你可以使用命令行上的-P标志激活配置文件。
提醒:
如果你不想使用spring-boot-starter-parent,你需要从spring-boot插件中为process-test-aot和Native Build Tools插件中为测试目标配置执行。
要构建images并运行测试,请在nativeTest配置文件处于活动状态的情况下使用测试目标:
$ gradle nativeTest
4、 Native Images 高级应用
4.1嵌套的properties配置
Spring AOT引擎会自动创建反射配置文件。但是,非内部类的嵌套配置properties必须用@NestedConfigurationProperty进行注释,否则将无法检测到它们,也无法绑定。
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@ConfigurationProperties(prefix = "my.properties")
public class MyProperties {
private String name;
@NestedConfigurationProperty
private final Nested nested = new Nested();
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Nested getNested() {
return this.nested;
}
}
其中Nested是:
public class Nested {
private int number;
public int getNumber() {
return this.number;
}
public void setNumber(int number) {
this.number = number;
}
}
上面的示例为my.properties.name和my.proporties.nested.number生成配置财产。如果嵌套字段上没有 @NestedConfigurationProperty 注释,my.propropertiex.nested_number 属性将无法在本机映像中绑定。
使用构造函数绑定时,必须使用@NestedConfigurationProperty对字段进行注释:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.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;
}
public String getName() {
return this.name;
}
public Nested getNested() {
return this.nested;
}
}
使用记录时,必须使用 @NestedConfigurationProperty 对参数进行注释:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@ConfigurationProperties(prefix = "my.properties")
public record MyPropertiesRecord(String name, @NestedConfigurationProperty Nested nested) {
}
使用Kotlin时,需要使用@NestedConfigurationProperty注释数据类的参数:
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.NestedConfigurationProperty
@ConfigurationProperties(prefix = "my.properties")
data class MyPropertiesKotlin(
val name: String,
@NestedConfigurationProperty val nested: Nested
)
注意:
请在所有情况下使用public getter和setter,否则proporties将不可绑定。
4.2 将jar转为一个可执行的本地iamge
只要jar包含AOT生成的资产,就可以将Spring Boot可执行jar转换为本地iamge。这可能是有用的,原因有很多,包括:
- 你可以保留常规的JVM管道,并将JVM应用程序转换为CI/CD平台上的本机iamge。
- 由于本机iamge不支持交叉编译,你可以保留一个与操作系统无关的部署工件,稍后将其转换为不同的操作系统体系结构。
你可以使用Cloud native Buildpacks或GraalVM附带的本地iamge工具将Spring Boot可执行jar转换为本地映iamge。
注意:
你的可执行jar必须包括AOT生成的资产,如生成的类和JSON提示文件。
4.2.1 使用Buildpacks
SpringBoot应用程序通常通过Maven(mvn-Spring-Boot:build-image)或Gradle(Gradle-bootBuildImage)集成使用云原生构建包。你也可以使用pack将经过AOT处理的Spring Boot可执行jar转换为本地 container image.。
首先,确保有一个Docker守护进程可用(有关更多详细信息,请参阅Get-Docker)。如果你在Linux上,请将其配置为允许非root用户。
你还需要按照buildpacks.io上的安装指南安装pack。
假设构建为myproject-0.0.1-SNAPSHOT.jar的经过AOT处理的Spring Boot可执行jar在目标目录中,则运行:
$ pack build --builder paketobuildpacks/builder:tiny \
--path target/myproject-0.0.1-SNAPSHOT.jar \
--env 'BP_NATIVE_IMAGE=true' \
my-application:0.0.1-SNAPSHOT
注意:
你不需要安装本地GraalVM就可以以这种方式生成映像。
打包完成后,你可以使用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这样的下载管理器。
假设构建为 myproject-0.0.1-SNAPSHOT.jar 的经过AOT处理的Spring Boot可执行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/nature image/argfile可能没有打包在jar中。只有当需要覆盖可达性元数据时,才会包含它。
警告:
nativ-image -cp标志不接受通配符。你需要确保列出了所有的jar(上面的命令使用find和tr来执行此操作)。
4.3 使用Tracing Agent
GraalVM native-image Tracing Agent允许你截获JVM上的反射、资源或代理使用情况,以便生成相关提示。Spring应该自动生成大多数提示,但可以使用Tracing Agent快速识别丢失的条目。
当使用代理为本地映像生成提示时,有几种方法:
- 直接启动应用程序并进行练习。
- 运行应用程序测试来练习应用程序。
当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停止应用程序。
在应用程序关闭时,native image tracing agetn会将提示文件写入给定的配置输出目录。你可以手动检查这些文件,也可以将它们用作native image构建过程的输入。要将它们用作输入,请将它们复制到src/main/resources/META-INF/nature image/目录中。下次构建本机映像时,GraalVM将考虑这些文件。
可以在native iamge tracing agent 代理上设置更高级的选项,例如通过调用程序类过滤录制的提示等。有关进一步的阅读,请参阅官方文档。
4.4 自定义Hint
如果你需要为反射、资源、序列化、代理使用等提供自己的提示,你可以使用RuntimeHintsRegisterAPI。创建一个实现RuntimeHintsRegistrar接口的类,然后对提供的RuntimeHints实例进行适当的调用:
import java.lang.reflect.Method;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.util.ReflectionUtils;
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);
}
}
然后,你可以在任何 @Configuration 类(例如你的@SpringBootApplication注释的应用程序类)上使用 @ImportRuntimeHints 来激活这些提示。
如果你有需要绑定的类(在序列化或反序列化JSON时最需要),则可以在任何bean上使用@RegisterReflectionForBinding。大多数提示都是自动推断的,例如,当接受或返回@RestController方法的数据时。但是,当您直接使用WebClient或RestTemplate时,你可能需要使用@RegisterReflectionForBinding。
4.4.1. 测试自定义Hint
RuntimeHintsPredicates API可用于测试提示。API提供了构建测试的方法,该方法可用于测试RuntimeHints实例。
如果你使用的是AssertJ,那么你的测试将如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.boot.docs.nativeimage.advanced.customhints.MyRuntimeHints;
import static org.assertj.core.api.Assertions.assertThat;
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 native image是一种不断发展的技术,并非所有库都提供支持。GraalVM社区通过为尚未发布自己的项目提供可达性元数据来提供帮助。Spring本身不包含第三方库的提示,而是依赖于可达性元数据项目。
如果你在为Spring Boot应用程序生成native image 时遇到问题,请查看Spring Boot wiki的Spring Boot with GraalVM页面。你还可以向GitHub上的spring-aot-smoke-tests项目提交问题,该项目用于确认常见的应用程序类型是否按预期工作。
如果您发现一个库不适用于GraalVM,请提出可达性元数据项目的问题。
5、接下来要读什么
如果你想了解更多关于我们的构建插件提供的提前处理的信息,请参阅Maven和Gradle插件文档。要了解有关用于执行处理的API的更多信息,请浏览Spring Framework源代码的org.springframework.aot.generate和org.springfframework.beans.factory.aot包。
有关Spring和GraalVM的已知限制,请参阅Spring Boot wiki。