此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Boot 4.0.4!spring-doc.cadn.net.cn

使用注解处理器生成您自己的元数据

您可以轻松地通过使用 spring-boot-configuration-processor JAR 包,从使用 @ConfigurationProperties 注解标记的项中生成自己的配置元数据文件。 该 JAR 包包含一个 Java 注解处理器,会在编译项目时被调用。spring-doc.cadn.net.cn

配置注解处理器

使用 Maven 构建时,请将编译器插件(3.12.0 或更高版本)配置为在注解处理器路径中添加 spring-boot-configuration-processorspring-doc.cadn.net.cn

<project>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.springframework.boot</groupId>
							<artifactId>spring-boot-configuration-processor</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

使用 Gradle 时,应在 annotationProcessor 配置中声明依赖项,如下例所示:spring-doc.cadn.net.cn

dependencies {
	annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

如果您使用的是 additional-spring-configuration-metadata.json 文件,则应将 compileJava 任务配置为依赖于 processResources 任务,如下例所示:spring-doc.cadn.net.cn

tasks.named('compileJava') {
	inputs.files(tasks.named('processResources'))
}

该依赖项可确保在编译期间注解处理器运行时,额外的元数据可用。spring-doc.cadn.net.cn

如果在项目中使用 AspectJ,则需要确保注解处理器仅运行一次。 实现此目标有多种方法。 在 Maven 中,您可以显式配置 maven-apt-plugin,并仅在此处添加注解处理器的依赖项。 您也可以让 AspectJ 插件执行全部处理工作,并在 maven-compiler-plugin 配置中禁用注解处理,如下所示:spring-doc.cadn.net.cn

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<configuration>
		<proc>none</proc>
	</configuration>
</plugin>

如果在项目中使用 Lombok,则需要确保其注解处理器在 spring-boot-configuration-processor 之前运行。 若使用 Maven,可通过 Maven 编译器插件的 annotationProcessors 属性,按所需顺序列出注解处理器。 若使用 Gradle,则需在 annotationProcessor 配置中按所需顺序声明依赖项。spring-doc.cadn.net.cn

自动元数据生成

处理器会处理所有用 @ConfigurationProperties 注解的类和方法。它还会处理所有用 @ConfigurationPropertiesSource 注解的类spring-doc.cadn.net.cn

自定义注解,如果它们被以上任一注解进行元注解,则不被支持。

如果该类仅有一个带参数的构造函数,则会为每个构造函数参数创建一个属性,除非该构造函数上标注了 @Autowired。 如果该类具有一个显式标注了 @ConstructorBinding 的构造函数,则会为此构造函数的每个参数创建一个属性。 否则,将通过标准的 getter 和 setter 方法来发现属性,并对集合(Collection)和映射(Map)类型进行特殊处理(即使仅有 getter 方法存在,也能检测到这些类型)。 注解处理器还支持使用 Lombok 的 @Data@Value@Getter@Setter 注解。spring-doc.cadn.net.cn

请考虑以下示例:spring-doc.cadn.net.cn

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

@ConfigurationProperties("my.server")
public class MyServerProperties {

	/**
	 * Name of the server.
	 */
	private String name;

	/**
	 * IP address to listen to.
	 */
	private String ip = "127.0.0.1";

	/**
	 * Port to listener to.
	 */
	private int port = 9797;

	// getters/setters ...

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

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

	public String getIp() {
		return this.ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public int getPort() {
		return this.port;
	}

	public void setPort(int port) {
		this.port = port;
	}

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

@ConfigurationProperties("my.server")
class MyServerProperties(

	/**
	 * Name of the server.
	 */
	var name: String,

	/**
	 * IP address to listen to.
	 */
	var ip: String = "127.0.0.1",

	/**
	 * Port to listen to.
	 */
	var port: Int = 9797)

这暴露了三个属性,其中 my.server.name 没有默认值,而 my.server.ipmy.server.port 的默认值分别为 "127.0.0.1"9797。 字段上的 Javadoc 用于填充 description 属性。 例如,my.server.ip 的描述是“要监听的 IP 地址。”。spring-doc.cadn.net.cn

description 属性只能在类型作为正在编译的源代码时填充。 当类型仅作为依赖项中的编译类可用时,它将不会被填充。 对于这些情况,您可以 获取元数据源提供手动条目spring-doc.cadn.net.cn

您应仅对字段 Javadoc 使用纯文本并设置 `@ConfigurationProperties`,因为这些内容在添加到 JSON 之前不会被处理。

若在记录类(record class)中使用 @ConfigurationProperties,则记录组件的描述应通过类级别的 Javadoc 标签 @param 提供(记录类中不存在显式的实例字段,因此无法在字段级别添加常规的 Javadoc 注释)。spring-doc.cadn.net.cn

注解处理器会应用多种启发式规则,从源代码模型中提取默认值。 仅当类型以正在编译的源代码形式存在时,才能提取默认值。 如果类型仅作为依赖项中的已编译类存在,则无法提取默认值。 此外,必须以静态方式提供默认值。 特别需要注意的是,不得引用其他类中定义的常量。 同时,注解处理器无法自动检测 Collections 的默认值。spring-doc.cadn.net.cn

对于无法自动检测默认值的情况,应提供手动元数据。 请参考以下示例:spring-doc.cadn.net.cn

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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

@ConfigurationProperties("my.messaging")
public class MyMessagingProperties {

	private List<String> addresses = new ArrayList<>(Arrays.asList("a", "b"));

	private ContainerType containerType = ContainerType.SIMPLE;

	// getters/setters ...

	public List<String> getAddresses() {
		return this.addresses;
	}

	public void setAddresses(List<String> addresses) {
		this.addresses = addresses;
	}

	public ContainerType getContainerType() {
		return this.containerType;
	}

	public void setContainerType(ContainerType containerType) {
		this.containerType = containerType;
	}

	public enum ContainerType {

		SIMPLE, DIRECT

	}

}
import org.springframework.boot.context.properties.ConfigurationProperties
import java.util.Arrays

@ConfigurationProperties("my.messaging")
class MyMessagingProperties(

	val addresses: List<String> = ArrayList(Arrays.asList("a", "b")),

	var containerType: ContainerType = ContainerType.SIMPLE) {

	enum class ContainerType {
		SIMPLE, DIRECT
	}
}

为了在上述类中记录属性的默认值,您可以在该模块的手动元数据中添加以下内容:spring-doc.cadn.net.cn

{"properties": [
	{
		"name": "my.messaging.addresses",
		"defaultValue": ["a", "b"]
	},
	{
		"name": "my.messaging.container-type",
		"defaultValue": "simple"
	}
]}
仅需使用该属性的 name 即可为现有属性记录额外的元数据。

嵌套属性

该注解处理器会自动将内部类视为嵌套属性。 我们无需在命名空间根级别上分别记录 ipport,而是可以为其创建一个子命名空间。 请参阅更新后的示例:spring-doc.cadn.net.cn

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

@ConfigurationProperties("my.server")
public class MyServerProperties {

	private String name;

	private Host host;

	// getters/setters ...

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

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

	public Host getHost() {
		return this.host;
	}

	public void setHost(Host host) {
		this.host = host;
	}

	public static class Host {

		private String ip;

		private int port;

		// getters/setters ...

		public String getIp() {
			return this.ip;
		}

		public void setIp(String ip) {
			this.ip = ip;
		}

		public int getPort() {
			return this.port;
		}

		public void setPort(int port) {
			this.port = port;
		}

	}

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

@ConfigurationProperties("my.server")
class MyServerProperties(
	var name: String,
	var host: Host) {

	class Host(val ip: String, val port: Int = 0)

}

上述示例为 my.server.namemy.server.host.ipmy.server.host.port 属性生成元数据信息。 您可以在字段或 getter 方法上使用 @NestedConfigurationProperty 注解,以指示将一个普通(非内部)类视为嵌套类来处理。spring-doc.cadn.net.cn

这对集合(collections)和映射(maps)没有影响,因为这些类型会自动被识别,并且会分别为它们生成一个元数据属性。

配置属性源

如果在某个带有 @ConfigurationProperties 注解的类型中使用了另一个模块中的类型,某些元数据元素无法自动发现。 重复使用上面的例子,如果 Host 位于另一个模块中,则无法获得完整的元数据,因为注解处理器无法访问 Host 的源代码。spring-doc.cadn.net.cn

要处理此用例,请在包含 Host 类型的模块中添加注解处理器,并用 @ConfigurationPropertiesSource 进行注解:spring-doc.cadn.net.cn

import org.springframework.boot.context.properties.ConfigurationPropertiesSource;

@ConfigurationPropertiesSource
public class Host {

	/**
	 * IP address to listen to.
	 */
	private String ip = "127.0.0.1";

	/**
	 * Port to listener to.
	 */
	private int port = 9797;

	// getters/setters ...

	public String getIp() {
		return this.ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public int getPort() {
		return this.port;
	}

	public void setPort(int port) {
		this.port = port;
	}

}
import org.springframework.boot.context.properties.ConfigurationPropertiesSource

@ConfigurationPropertiesSource
class Host {

	/**
	 * IP address to listen to.
	 */
	var ip: String = "127.0.0.1"

	/**
	 * Port to listener to.
	 */
	var port = 9797

}

这会为HostMETA-INF/spring/configuration-metadata/com.example.Host.json中生成元数据,并且当注解处理器处理此类类型时会自动重复使用。spring-doc.cadn.net.cn

您还可以对位于另一个模块中的父类进行注解,该类被一个<a t="C1"><code>0</code></a>注解的类型所继承。spring-doc.cadn.net.cn

如果你需要重新使用你无法控制的类型的元数据,请创建一个名称符合上述模式的文件,只要它在类路径上可用,就会被使用。

添加其他元数据

Spring Boot 的配置文件处理机制非常灵活,通常情况下,配置属性可能存在于未绑定到 @ConfigurationProperties Bean 的场景中。 您可能还需要调整某个现有配置项的某些属性,或者完全忽略该配置项。 为支持此类情况并允许您提供自定义的“提示”,注解处理器会自动将 META-INF/additional-spring-configuration-metadata.json 中的条目合并到主元数据文件中。spring-doc.cadn.net.cn

在为一个类型生成源元数据时,你还可以为该类型创建自定义元数据,例如 com.example.SomeType,在 META-INF/spring/configuration/metadata/com.example.SomeType.json 中。spring-doc.cadn.net.cn

如果引用了已自动检测到的属性,则会覆盖其描述、默认值和弃用信息(如已指定)。 如果当前模块中未识别到该手动属性声明,则将其作为新属性添加。spring-doc.cadn.net.cn

附加元数据文件的格式与常规 spring-configuration-metadata.json 完全相同。 “ignored.properties” 部分中的条目将从生成的 spring-configuration-metadata.json 文件的 “properties” 部分中删除。spring-doc.cadn.net.cn

附加的属性文件是可选的。 如果没有附加属性,请不要添加该文件。spring-doc.cadn.net.cn