|
如需获取最新稳定版本,请使用 Spring Boot 4.0.4! |
使用注解处理器生成您自己的元数据
您可以轻松地通过使用 spring-boot-configuration-processor JAR 包,从使用 @ConfigurationProperties 注解标记的项中生成自己的配置元数据文件。
该 JAR 包包含一个 Java 注解处理器,会在编译项目时被调用。
配置注解处理器
使用 Maven 构建时,请将编译器插件(3.12.0 或更高版本)配置为在注解处理器路径中添加 spring-boot-configuration-processor:
<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 配置中声明依赖项,如下例所示:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}
如果您使用的是 additional-spring-configuration-metadata.json 文件,则应将 compileJava 任务配置为依赖于 processResources 任务,如下例所示:
tasks.named('compileJava') {
inputs.files(tasks.named('processResources'))
}
该依赖项可确保在编译期间注解处理器运行时,额外的元数据可用。
|
如果在项目中使用 AspectJ,则需要确保注解处理器仅运行一次。
实现此目标有多种方法。
在 Maven 中,您可以显式配置
|
|
如果在项目中使用 Lombok,则需要确保其注解处理器在 |
自动元数据生成
该处理器会识别所有使用 @ConfigurationProperties 注解的类和方法。
不支持使用 @ConfigurationProperties 进行元注解的自定义注解。 |
如果该类仅有一个带参数的构造函数,则会为每个构造函数参数创建一个属性,除非该构造函数上标注了 @Autowired。
如果该类具有一个显式标注了 @ConstructorBinding 的构造函数,则会为此构造函数的每个参数创建一个属性。
否则,将通过标准的 getter 和 setter 方法来发现属性,并对集合(Collection)和映射(Map)类型进行特殊处理(即使仅有 getter 方法存在,也能检测到这些类型)。
注解处理器还支持使用 Lombok 的 @Data、@Value、@Getter 和 @Setter 注解。
请考虑以下示例:
-
Java
-
Kotlin
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.ip 和 my.server.port 的默认值分别为 "127.0.0.1" 和 9797。
字段上的 Javadoc 用于填充 description 属性。
例如,my.server.ip 的描述为“监听的 IP 地址”。
仅当类型以源代码形式存在且正在被编译时,description 属性才能被填充。
当类型仅作为依赖项中的已编译类提供时,则无法填充该属性。
对于此类情况,应提供手动元数据。
您应仅对字段 Javadoc 使用纯文本并设置 `@ConfigurationProperties`,因为这些内容在添加到 JSON 之前不会被处理。 |
若在记录类(record class)中使用 @ConfigurationProperties,则记录组件的描述应通过类级别的 Javadoc 标签 @param 提供(记录类中不存在显式的实例字段,因此无法在字段级别添加常规的 Javadoc 注释)。
注解处理器会应用多种启发式规则,从源代码模型中提取默认值。
仅当类型以正在编译的源代码形式存在时,才能提取默认值。
如果类型仅作为依赖项中的已编译类存在,则无法提取默认值。
此外,必须以静态方式提供默认值。
特别需要注意的是,不得引用其他类中定义的常量。
同时,注解处理器无法自动检测 Collections 的默认值。
对于无法自动检测默认值的情况,应提供手动元数据。 请参考以下示例:
-
Java
-
Kotlin
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
}
}
为了在上述类中记录属性的默认值,您可以在该模块的手动元数据中添加以下内容:
{"properties": [
{
"name": "my.messaging.addresses",
"defaultValue": ["a", "b"]
},
{
"name": "my.messaging.container-type",
"defaultValue": "simple"
}
]}
仅需使用该属性的 name 即可为现有属性记录额外的元数据。 |
嵌套属性
该注解处理器会自动将内部类视为嵌套属性。
我们无需在命名空间根级别上分别记录 ip 和 port,而是可以为其创建一个子命名空间。
请参阅更新后的示例:
-
Java
-
Kotlin
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.ip 和 my.server.host.port 属性生成元数据信息。
您可以在字段或 getter 方法上使用 @NestedConfigurationProperty 注解,以指示将一个普通(非内部)类视为嵌套类来处理。
| 这对集合(collections)和映射(maps)没有影响,因为这些类型会自动被识别,并且会分别为它们生成一个元数据属性。 |
添加其他元数据
Spring Boot 的配置文件处理机制非常灵活,通常情况下,配置属性可能存在于未绑定到 @ConfigurationProperties Bean 的场景中。
您可能还需要调整某个现有配置项的某些属性,或者完全忽略该配置项。
为支持此类情况并允许您提供自定义的“提示”,注解处理器会自动将 META-INF/additional-spring-configuration-metadata.json 中的条目合并到主元数据文件中。
如果引用了已自动检测到的属性,则会覆盖其描述、默认值和弃用信息(如已指定)。 如果当前模块中未识别到该手动属性声明,则将其作为新属性添加。
additional-spring-configuration-metadata.json 文件的格式与常规 spring-configuration-metadata.json 文件完全相同。
“ignored.properties”(已忽略的属性)部分中包含的条目将从生成的 spring-configuration-metadata.json 文件的 “properties”(属性)部分中移除。
附加的属性文件是可选的。 如果没有附加属性,请不要添加该文件。