高级原生图像主题

嵌套配置属性

Spring 预置引擎会自动为配置属性创建反射提示。 然而,非内类的嵌套配置属性必须注释为@NestedConfigurationProperty否则它们不会被检测到,也无法绑定。spring-doc.cadn.net.cn

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@ConfigurationProperties("my.properties")
public class MyProperties {

	private String name;

	@NestedConfigurationProperty
	private final Nested nested = new Nested();

	// getters / setters...

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Nested getNested() {
		return this.nested;
	}

}

哪里嵌 套是:spring-doc.cadn.net.cn

public class Nested {

	private int number;

	// getters / setters...

	public int getNumber() {
		return this.number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

}
class Nested {
}

上述示例产生了 的配置性质my.properties.namemy.properties.nested.number. 没有@NestedConfigurationProperty关于嵌 套字段,my.properties.nested.number属性在原生图片中无法绑定。 你也可以对getter方法进行注释。spring-doc.cadn.net.cn

使用构造函数绑定时,你必须对字段进行注释@NestedConfigurationProperty:spring-doc.cadn.net.cn

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@ConfigurationProperties("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;
	}

	// getters / setters...

	public String getName() {
		return this.name;
	}

	public Nested getNested() {
		return this.nested;
	}

}

使用记录时,必须用 来注释参数@NestedConfigurationProperty:spring-doc.cadn.net.cn

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@ConfigurationProperties("my.properties")
public record MyPropertiesRecord(String name, @NestedConfigurationProperty Nested nested) {

}

使用 Kotlin 时,你需要用 来注释数据类的参数@NestedConfigurationProperty:spring-doc.cadn.net.cn

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.NestedConfigurationProperty

@ConfigurationProperties("my.properties")
data class MyPropertiesKotlin(
	val name: String,
	@NestedConfigurationProperty val nested: Nested
)
请在所有情况下使用公开的获取者和设定者,否则属性将无法绑定。

转换 Spring Boot 可执行 jar

只要jar包含AOT生成的资产,就可以将Spring Boot的可执行文件jar转换为原生镜像。 这有多种用途,包括:spring-doc.cadn.net.cn

你可以用Cloud Native Buildpacks将Spring Boot的可执行文件jar转换成原生镜像,或者使用原生图像该工具随GraalVM一同发布。spring-doc.cadn.net.cn

你的可执行 jar 必须包含 AOT 生成的资源,比如生成的类和 JSON 提示文件。

使用 Buildpack

Spring Boot 应用程序通常通过 Maven 使用 Cloud Native 构建包(MVN Spring-boot:build-image)或Gradle(gradle bootBuildImage)整合。 不过,你也可以使用将AOT处理的Spring Boot可执行文件jar转换为本地容器镜像。spring-doc.cadn.net.cn

你必须至少用 JDK 25 构建你的应用,因为构建包使用的是和编译用的 Java 版本相同的 GraalVM 原生映像版本。

首先,确保有 Docker 守护进程可用(详情请参见获取 Docker )。如果你用的是Linux,可以设置成允许非root用户访问spring-doc.cadn.net.cn

你还需要安装通过按照 buildpacks.io 上的安装指南作。spring-doc.cadn.net.cn

假设AOT处理了Spring Boot可执行文件jar,构建为myproject-0.0.1-SNAPSHOT.jar属于目标目录,运行:spring-doc.cadn.net.cn

$ pack build --builder paketobuildpacks/builder-noble-java-tiny \
    --path target/myproject-0.0.1-SNAPSHOT.jar \
    --env 'BP_NATIVE_IMAGE=true' \
    my-application:0.0.1-SNAPSHOT
你不需要本地 GraalVM 安装就能以这种方式生成映像。

一次完成后,你可以用以下方式启动应用Docker 运行:spring-doc.cadn.net.cn

$ docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT

使用 GraalVM 原生映像

将AOT处理的Spring Boot可执行文件jar转换为原生可执行文件的另一种方法是使用GraalVM原生图像工具。 要实现这一点,你需要在你的机器上安装 GraalVM 发行版。 你可以在Liberica原生图像套件页面手动下载,或者使用像SDKMAN!这样的下载管理器。spring-doc.cadn.net.cn

假设AOT处理了Spring Boot可执行文件jar,构建为myproject-0.0.1-SNAPSHOT.jar属于目标目录,运行:spring-doc.cadn.net.cn

$ 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可能不会装在你的罐子里。 只有在需要覆盖可达元数据时才会包含此功能。
原生图像 -cp旗帜不接受万能牌。 你需要确保所有罐子都被列出(上面的命令是找到TR做到这一点)。

使用 Tracing Agent

GraalVM原生图像追踪代理允许你拦截JVM上的反射、资源或代理使用情况,以生成相关的提示。 Spring 应该会自动生成大部分线索,但追踪工具可以快速识别缺失的条目。spring-doc.cadn.net.cn

在使用代理生成原生图像提示时,有几种方法:spring-doc.cadn.net.cn

第一种方法很有意思,可以当Spring无法识别某个库或图案时,识别缺失的提示。spring-doc.cadn.net.cn

第二个选项听起来更适合重复设置,但默认生成的提示会包含测试基础设施所需的所有内容。 当应用真正运行时,其中一些将不再必要。 为解决这个问题,代理支持一个访问过滤文件,该文件会将某些数据排除在生成输出中。spring-doc.cadn.net.cn

直接启动应用

请使用以下命令启动带有本地图像追踪代理的应用程序:spring-doc.cadn.net.cn

$ 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.spring-doc.cadn.net.cn

应用关闭时,本地图像追踪代理会将提示文件写入给定的配置输出目录。 你可以手动检查这些文件,或者将它们作为本地图像构建过程的输入。 要用作输入,复制到src/main/resources/META-INF/native-image/目录。 下次你构建原生镜像时,GraalVM 会考虑这些文件。spring-doc.cadn.net.cn

本地图像追踪代理上还可以设置更高级的选项,例如按调用类过滤录制提示等。 如需进一步阅读,请参阅官方文档spring-doc.cadn.net.cn

自定义提示

如果你需要为反射、资源、序列化、代理使用等提供自己的提示,你可以使用RuntimeHintsRegistrar应用程序接口。 创建一个实现RuntimeHintsRegistrar然后对所提供的 进行适当调用。运行提示实例:spring-doc.cadn.net.cn

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

}

然后你可以使用@ImportRuntimeHints在任意@Configuration类(例如你的@SpringBootApplication注释应用类)来激活这些提示。spring-doc.cadn.net.cn

如果你有需要绑定的类(主要是序列化或反序列化 JSON 时需要),你可以用@RegisterReflectionForBinding任何一颗豆子。 大多数提示都是自动推断的,例如在接受或返回来自@RestController方法。 但当你与Web客户端,Rest客户端Rest模板直接来说,你可能需要使用@RegisterReflectionForBinding.spring-doc.cadn.net.cn

测试自定义提示

运行提示谓词API可以用来测试你的提示。 该 API 提供了构建谓语可以用来测试运行提示实例。spring-doc.cadn.net.cn

如果你用的是 AssertJ,你的测试会是这样的:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.boot.docs.packaging.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);
	}

}

静态提供提示

如果你愿意,也可以在一个或多个GraalVM JSON提示文件中静态提供自定义提示。 此类文件应被存放于src/main/resources/元-INF/原生图像/*/*/目录。 AOT处理过程中生成的提示会写入一个名为META-INF/native-image/{groupId}/{artifactId}/. 将静态提示文件放置在与该位置不冲突的目录中,例如META-INF/native-image/{groupId}/{artifactId}-additional-hints/.spring-doc.cadn.net.cn

已知的局限性

GraalVM 原生镜像是一项不断发展的技术,并非所有库都支持。 GraalVM 社区通过为尚未发布的项目提供可达元数据来提供帮助。 Spring本身不包含第三方库的提示,而是依赖可达性元数据项目。spring-doc.cadn.net.cn

如果您在为 Spring Boot 应用程序生成原生镜像时遇到问题,请查看 Spring Boot 维基中的 Spring Boot with GraalVM 页面。 你也可以向GitHub上的spring-aot-smoke-tests项目贡献问题,该项目用于确认常见应用类型是否按预期运行。spring-doc.cadn.net.cn

如果你发现某个库不支持 GraalVM,请在可达性元数据项目中提出问题。spring-doc.cadn.net.cn