此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.4.3spring-doc.cadn.net.cn

GraalVM 原生镜像简介

GraalVM 原生映像提供了一种部署和运行 Java 应用程序的新方法。 与 Java 虚拟机相比,本机映像可以以更小的内存占用和更快的启动时间运行。spring-doc.cadn.net.cn

它们非常适合使用容器映像部署的应用程序,在与“功能即服务”(FaaS) 平台结合使用时尤其有趣。spring-doc.cadn.net.cn

与为 JVM 编写的传统应用程序不同,GraalVM Native Image 应用程序需要提前处理才能创建可执行文件。 这种预先处理涉及从应用程序代码的主入口点静态分析应用程序代码。spring-doc.cadn.net.cn

GraalVM Native Image 是一个完整的、特定于平台的可执行文件。 您无需提供 Java 虚拟机即可运行本机映像。spring-doc.cadn.net.cn

如果您只想开始并试用 GraalVM,可以跳转到开发您的第一个 GraalVM 原生应用程序部分,稍后再返回此部分。

与 JVM 部署的主要区别

GraalVM Native Images 是提前生成的,这意味着原生应用和基于 JVM 的应用之间存在一些关键差异。 主要区别在于:spring-doc.cadn.net.cn

除了这些差异之外, Spring 还使用了一个称为 Spring Ahead-of-Time processing 的过程,这施加了进一步的限制。 请务必至少阅读下一部分的开头部分以了解这些内容。spring-doc.cadn.net.cn

GraalVM 参考文档的本机映像兼容性指南部分提供了有关 GraalVM 限制的更多详细信息。

了解 Spring 预先处理

典型的 Spring Boot 应用程序是相当动态的,配置是在运行时执行的。 事实上, Spring Boot 自动配置的概念在很大程度上取决于对运行时的状态做出反应,以便正确配置。spring-doc.cadn.net.cn

尽管可以告诉 GraalVM 应用程序的这些动态方面,但这样做会抵消静态分析的大部分好处。 因此,当使用 Spring Boot 创建本机映像时,会假定一个封闭世界,并且应用程序的动态方面受到限制。spring-doc.cadn.net.cn

除了 GraalVM 本身产生的限制外,封闭世界假设还意味着以下限制:spring-doc.cadn.net.cn

当这些限制到位时,Spring 可以在构建时执行提前处理,并生成 GraalVM 可以使用的其他资产。 Spring AOT 处理的应用程序通常会生成:spring-doc.cadn.net.cn

如果生成的提示不够,您还可以提供自己的提示。spring-doc.cadn.net.cn

源代码生成

Spring 应用程序由 Spring Bean 组成。 在内部, Spring Framework 使用两个不同的概念来管理 bean。 有 bean 实例,它们是已经创建的实际实例,可以注入到其他 bean 中。 还有一些 bean 定义,用于定义 bean 的属性以及应如何创建其实例。spring-doc.cadn.net.cn

如果我们采用典型的@Configuration类:spring-doc.cadn.net.cn

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();
	}

}

The bean definition is created by parsing the @Configuration class and finding the @Bean methods. In the above example, we’re defining a BeanDefinition for a singleton bean named myBean. We’re also creating a BeanDefinition for the MyConfiguration class itself.spring-doc.cadn.net.cn

When the myBean instance is required, Spring knows that it must invoke the myBean() method and use the result. When running on the JVM, @Configuration class parsing happens when your application starts and @Bean methods are invoked using reflection.spring-doc.cadn.net.cn

When creating a native image, Spring operates in a different way. Rather than parsing @Configuration classes and generating bean definitions at runtime, it does it at build-time. Once the bean definitions have been discovered, they are processed and converted into source code that can be analyzed by the GraalVM compiler.spring-doc.cadn.net.cn

The Spring AOT process would convert the configuration class above to code like this:spring-doc.cadn.net.cn

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;
	}

}
The exact code generated may differ depending on the nature of your bean definitions.

You can see above that the generated code creates equivalent bean definitions to the @Configuration class, but in a direct way that can be understood by GraalVM.spring-doc.cadn.net.cn

There is a bean definition for the myConfiguration bean, and one for myBean. When a myBean instance is required, a BeanInstanceSupplier is called. This supplier will invoke the myBean() method on the myConfiguration bean.spring-doc.cadn.net.cn

During Spring AOT processing, your application is started up to the point that bean definitions are available. Bean instances are not created during the AOT processing phase.

Spring AOT will generate code like this for all your bean definitions. It will also generate code when bean post-processing is required (for example, to call @Autowired methods). An ApplicationContextInitializer will also be generated which will be used by Spring Boot to initialize the ApplicationContext when an AOT processed application is actually run.spring-doc.cadn.net.cn

Although AOT generated source code can be verbose, it is quite readable and can be helpful when debugging an application. Generated source files can be found in target/spring-aot/main/sources when using Maven and build/generated/aotSources with Gradle.

Hint File Generation

In addition to generating source files, the Spring AOT engine will also generate hint files that are used by GraalVM. Hint files contain JSON data that describes how GraalVM should deal with things that it can’t understand by directly inspecting the code.spring-doc.cadn.net.cn

For example, you might be using a Spring annotation on a private method. Spring will need to use reflection in order to invoke private methods, even on GraalVM. When such situations arise, Spring can write a reflection hint so that GraalVM knows that even though the private method isn’t called directly, it still needs to be available in the native image.spring-doc.cadn.net.cn

Hint files are generated under META-INF/native-image where they are automatically picked up by GraalVM.spring-doc.cadn.net.cn

Generated hint files can be found in target/spring-aot/main/resources when using Maven and build/generated/aotResources with Gradle.

Proxy Class Generation

Spring sometimes needs to generate proxy classes to enhance the code you’ve written with additional features. To do this, it uses the cglib library which directly generates bytecode.spring-doc.cadn.net.cn

When an application is running on the JVM, proxy classes are generated dynamically as the application runs. When creating a native image, these proxies need to be created at build-time so that they can be included by GraalVM.spring-doc.cadn.net.cn

Unlike source code generation, generated bytecode isn’t particularly helpful when debugging an application. However, if you need to inspect the contents of the .class files using a tool such as javap you can find them in target/spring-aot/main/classes for Maven and build/generated/aotClasses for Gradle.