| 此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Boot 3.5.5! | 
GraalVM 原生镜像简介
GraalVM 本机映像提供了一种部署和运行 Java 应用程序的新方法。 与 Java 虚拟机相比,本机映像可以以更小的内存占用和更快的启动时间运行。
它们非常适合使用容器映像部署的应用程序,并且与“功能即服务”(FaaS) 平台结合使用时特别有趣。
与为 JVM 编写的传统应用程序不同,GraalVM 本机映像应用程序需要提前处理才能创建可执行文件。 这种提前处理涉及从其主入口点静态分析您的应用程序代码。
GraalVM 本机映像是一个完整的、特定于平台的可执行文件。 您无需提供 Java 虚拟机即可运行本机映像。
| 如果您只想开始并试验 GraalVM,可以跳转到开发您的第一个 GraalVM 本机应用程序部分,稍后返回此部分。 | 
与 JVM 部署的主要区别
GraalVM 本机映像是提前生成的,这意味着本机应用程序和基于 JVM 的应用程序之间存在一些关键差异。 主要区别是:
- 
应用程序的静态分析在构建时从 main入口点。
- 
创建本机映像时无法访问的代码将被删除,并且不会成为可执行文件的一部分。 
- 
GraalVM 不直接了解代码的动态元素,必须被告知反射、资源、序列化和动态代理。 
- 
应用程序类路径在构建时是固定的,不能更改。 
- 
没有延迟类加载,可执行文件中提供的所有内容都将在启动时加载到内存中。 
- 
Java 应用程序的某些方面存在一些不完全支持的限制。 
除了这些差异之外,Spring 还使用了一种称为 Spring Ahead-of-Time 处理的过程,这会施加进一步的限制。 请务必至少阅读下一节的开头以了解这些内容。
| GraalVM 参考文档的“本机映像兼容性指南”部分提供了有关 GraalVM 限制的更多详细信息。 | 
了解 Spring 提前处理
典型的 Spring Boot 应用程序是非常动态的,配置在运行时执行。 事实上,Spring Boot 自动配置的概念在很大程度上取决于对运行时状态的反应,以便正确配置事物。
尽管可以告诉 GraalVM 应用程序的这些动态方面,但这样做会抵消静态分析的大部分好处。 因此,相反,当使用 Spring Boot 创建本机映像时,假设是一个封闭的世界,并且应用程序的动态方面受到限制。
除了 GraalVM 本身创建的限制外,封闭世界假设还意味着以下限制:
- 
应用程序中定义的 bean 不能在运行时更改,这意味着: 
- 
不支持在创建 Bean 时更改的属性(例如, @ConditionalOnProperty和.enabled属性)。
 
当这些限制到位时,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)
 
- 
如果生成的提示不够,您也可以提供自己的提示。
源代码生成
Spring 应用程序由 Spring Beans 组成。 在内部,Spring Framework 使用两个不同的概念来管理 bean。 有 bean 实例,它们是已经创建的实际实例,可以注入到其他 bean 中。 还有一些 bean 定义用于定义 bean 的属性以及如何创建其实例。
如果我们采用典型的@Configuration类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.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 进程会将上面的配置类转换为如下代码:
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;
	}
}| 生成的确切代码可能会有所不同,具体取决于 Bean 定义的性质。 | 
您可以在上面看到,生成的代码创建了与@Configuration类,但以 GraalVM 可以理解的直接方式。
有一个 bean 定义myConfigurationbean,一个用于myBean.
当myBeaninstance 是必需的,则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 一起。 | 
提示文件生成
除了生成源文件外,Spring AOT 引擎还将生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,这些数据描述了 GraalVM 应如何通过直接检查代码来处理它无法理解的内容。
例如,您可能在私有方法上使用 Spring 注释。 Spring 需要使用反射来调用私有方法,即使在 GraalVM 上也是如此。 当出现这种情况时,Spring 可以编写反射提示,以便 GraalVM 知道即使私有方法没有直接调用,它仍然需要在本机镜像中可用。
提示文件在META-INF/native-imageGraalVM 会自动获取它们。
| 生成的提示文件可以在 target/spring-aot/main/resources使用 Maven 和build/generated/aotResources与 Gradle 一起。 |