介绍GraalVM原生镜像

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

它们非常适合使用容器镜像部署的应用,尤其当与“功能即服务”(FaaS)平台结合时更具吸引力。spring-doc.cadn.net.cn

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

GraalVM 原生镜像是一个完整的、针对特定平台的可执行文件。 运行原生镜像不需要自带 Java 虚拟机。spring-doc.cadn.net.cn

如果你只是想开始并尝试使用 GraalVM,可以跳转到“开发你的第一个 GraalVM 原生应用”部分,稍后再回来查看。

与JVM部署的主要区别

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

  • 对你的应用进行静态分析是在构建时从主要入口。spring-doc.cadn.net.cn

  • 创建原生镜像时无法访问的代码会被删除,且不会成为可执行文件的一部分。spring-doc.cadn.net.cn

  • GraalVM 不会直接感知你代码中的动态元素,必须被告知关于反射、资源、序列化和动态代理的相关信息。spring-doc.cadn.net.cn

  • 应用类路径在构建时是固定的,不能更改。spring-doc.cadn.net.cn

  • 没有懒惰类加载,所有可执行文件里发布的内容都会在启动时加载到内存中。spring-doc.cadn.net.cn

  • Java应用的某些方面存在一些未被完全支持的限制。spring-doc.cadn.net.cn

除了这些差异外,Spring还使用一种称为Spring提前处理的工艺,这对此施加了更多限制。 请务必至少阅读下一节的开头,了解这些信息。spring-doc.cadn.net.cn

GraalVM 参考文档中的原生映像兼容性指南部分提供了关于 GraalVM 限制的更多细节。

理解春季提前处理

典型的 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 Framework 使用两种不同的概念来管理豆子。 有豆子实例,指的是被创建并可以注入其他豆子的实例。 还有Beans定义,用于定义豆子的属性及其实例应如何创建。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();
	}

}

豆子定义是通过解析@Configuration类并寻找@Bean方法。 在上述例子中,我们定义了一个豆子定义对于一个名为我的豆子. 我们还在创造一个豆子定义对于MyConfiguration课程本身。spring-doc.cadn.net.cn

我的豆子实例是必需的,Spring 知道它必须调用我的豆()方法并使用结果。 在JVM上运行时,@Configuration类解析发生在应用开始时,@Bean方法通过反射被调用。spring-doc.cadn.net.cn

在创建原生图像时,Spring 的运作方式不同。 而不是解析@Configuration在运行时生成类和生成 BEAN 定义,它在构建时完成。 一旦发现了豆子定义,它们会被处理并转换成源代码,供GraalVM编译器分析。spring-doc.cadn.net.cn

Spring AOT 进程会将上述配置类转换为如下代码: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;
	}

}
具体生成的代码可能会根据你豆子定义的性质而有所不同。

你可以看到,生成的代码创建了与@Configuration但以 GraalVM 能够直接理解的方式。spring-doc.cadn.net.cn

豆子对myConfiguration豆子,还有一个我的豆子. 当我的豆子需要实例,a豆实例提供商被叫去。 该提供商将调用我的豆()方法myConfiguration豆。spring-doc.cadn.net.cn

在春季AOT处理期间,您的应用程序会启动到豆子定义可用为止。 豆子实例不会在AOT处理阶段创建。

Spring AOT 会为你所有的 bean 定义生成类似的代码。 当需要豆后处理时(例如,调用)@Autowired方法)。 一应用上下文初始化器还将生成 ,Spring Boot 将用它初始化应用上下文当AOT处理后的应用程序实际运行时。spring-doc.cadn.net.cn

虽然AOT生成的源代码可能冗长,但相当易读,在调试应用程序时非常有用。 生成的源文件可在以下文件中找到目标/春季进攻/主线/来源当使用 Maven 和构建/生成/进攻的来源与Gradle合作。

提示文件生成

除了生成源文件外,Spring AOT 引擎还会生成 GraalVM 使用的提示文件。 提示文件包含 JSON 数据,描述 GraalVM 如何处理它通过直接检查代码无法理解的内容。spring-doc.cadn.net.cn

例如,你可能在私有方法上使用 Spring 注释。 Spring 需要使用反射来调用私有方法,即使是在 GraalVM 上。 当出现这种情况时,Spring 可以写出一个反射提示,让 GraalVM 知道即使私有方法没有直接调用,它仍然需要在本地镜像中可用。spring-doc.cadn.net.cn

提示文件由以下生成元-INF/原生图像这些数据会被 GraalVM 自动接收。spring-doc.cadn.net.cn

生成的提示文件可在目标/春季的攻击/主/资源当使用 Maven 和构建/生成/进攻资源与Gradle合作。

代理类生成

Spring有时需要生成代理类,以增强你编写的代码,添加额外功能。 为此,它使用直接生成字节码的cglib库。spring-doc.cadn.net.cn

当应用程序运行在JVM上时,代理类会在应用程序运行时动态生成。 在创建原生镜像时,这些代理需要在构建时创建,以便被 GraalVM 包含。spring-doc.cadn.net.cn

与源代码生成不同,生成字节码在调试应用时并不特别有用。 然而,如果您需要检查。类使用以下工具的文件JavaP你可以在目标/春季-AOT/主/职业对于Maven和构建/生成/进攻的职业为了Gradle。