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

你可以轻松地从标注为@ConfigurationProperties通过使用Spring Boot配置处理器罐。 jar 包含一个 Java 注释处理器,在你的项目编译过程中调用它。spring-doc.cadn.net.cn

注释处理器配置

使用 Maven 构建时,配置编译器插件(3.12.0 或更高版本)以添加内容Spring Boot配置处理器到注释处理器路径:spring-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,应在注释处理处理器配置,如下示例所示:spring-doc.cadn.net.cn

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

如果你正在使用additional-spring-configuration-metadata.json文件,该compileJava任务应配置为依赖于process资源任务,如下例所示: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编译器插件配置如下: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配置处理器. 要用 Maven 来实现,请按照所需的顺序列出注释处理器,使用注释处理器Maven编译器插件的属性。 用 Gradle,声明 中的依赖关系注释处理处理器按所需顺序配置。spring-doc.cadn.net.cn

自动元数据生成

处理器会接收被注释为@ConfigurationProperties.它还会选择带有注释的类@ConfigurationPropertiesSourcespring-doc.cadn.net.cn

不支持用这些注释进行元注释的自定义注释。

如果类只有一个参数化构造函数,则每个构造函数参数创建一个属性,除非构造函数注释为@Autowired. 如果该类有一个构造子,明确注释为@ConstructorBinding每个构造子参数为该构造子创建一个属性。 否则,性质是通过标准采集器和具有特殊收集和映射类型处理的集合者和定位器来发现的(即使只有一个采集器存在也能检测到)。 注释处理器还支持使用@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没有默认值,且我的服务器.ipmy.server.port默认为"127.0.0.1"9797分别。 字段上的 Javadoc 用于填充描述属性。 例如,描述我的服务器.ip是“可监听的IP地址”。spring-doc.cadn.net.cn

描述属性只有在该类型作为正在编译的源代码可用时才能填充。 当该类型仅作为依赖的编译类可用时,该类型不会被填充。 在这种情况下,你可以获取元数据手动录入spring-doc.cadn.net.cn

你应该只使用纯文本,且@ConfigurationProperties字段 Javadoc,因为它们在添加到 JSON 之前不会被处理。

如果你使用,@ConfigurationProperties对于记录类,那么记录组件的描述应通过类级Javadoc标签提供@param(记录类中没有显式实例字段来放置常规字段级 Javadocs。)spring-doc.cadn.net.cn

注释处理器应用多种启发式方法从源模型中提取默认值。 默认值只有在类型作为正在编译的源代码可用时才能提取。 当类型仅作为依赖的编译类可用时,它们不会被提取。 此外,默认值必须静态提供。 特别地,不要引用其他类中定义的常量。 此外,注释处理器无法自动检测 的默认值收集s.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"
	}
]}
只有名称该属性需要为现有属性记录额外的元数据。

嵌套性质

注释处理器会自动将内部类视为嵌套属性。 而不是记录知识产权端口在命名空间的根节点,我们可以为它创建一个子命名空间。 请考虑更新后的例子: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.name,my.server.host.ipmy.server.host.port性能。 你可以使用@NestedConfigurationProperty字段或getter方法上的注释,表示一个常规(非内部)类应被视为嵌套。spring-doc.cadn.net.cn

这对集合和映射没有影响,因为这些类型会自动识别,并且每个类型都会生成一个元数据属性。

配置属性来源

如果位于另一个模块中的类型被用于@ConfigurationProperties-注释类型,某些元数据元素无法自动发现。 重复使用上述例子,如果主机位于另一个模块中,由于注释处理器无法访问 的源,无法获得完整的元数据。主机.spring-doc.cadn.net.cn

为了处理此用例,可以在包含主机类型并注释为@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.ConfigurationPropertiesScan

@ConfigurationPropertiesScan
class Host {

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

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

}

这会生成 的元数据主机META-INF/spring/configuration-metadata/com.example.Host.json注释处理器处理此类类型时会自动重复使用。spring-doc.cadn.net.cn

你也可以注释位于另一个模块中的父类,该模块@ConfigurationProperties-注释型从 延伸。spring-doc.cadn.net.cn

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

添加额外元数据

Spring Boot 的配置文件处理非常灵活,通常存在不绑定于@ConfigurationProperties豆。 你还可能需要调整现有密钥的一些属性,或者完全忽略该密钥。 为了支持此类情况并允许你提供自定义“提示”,注释处理器会自动合并以下物品元步兵/additional-spring-configuration-metadata.json进入主元数据文件。spring-doc.cadn.net.cn

在生成某个类型的源元数据时,你也可以为该类型定制元数据,例如。com.example.SomeTypeMETA-INF/spring/configuration/metadata/com.example.SomeType.json.spring-doc.cadn.net.cn

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

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

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