使用注解处理器生成您自己的元数据
您可以通过使用 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 的存在来发现属性,并对集合和映射类型进行特殊处理(即使只有 getter 存在也能检测到)。
注解处理器还支持使用 @Data
、@Value
、@Getter
和 @Setter
lombok 注解。
考虑以下示例:
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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;
}
}
这将暴露三个属性,其中 my.server.name
没有默认值,而 my.server.ip
和 my.server.port
分别默认为 "127.0.0.1"
和 9797
。
字段上的 Javadoc 用于填充 description
属性。
例如,my.server.ip
的描述是 "IP address to listen to."。
只有当类型作为正在编译的源代码可用时,才能填充 description
属性。
当类型仅作为依赖项中的编译类可用时,将不会填充该属性。
对于这种情况,应该提供 手动元数据。
注意:您应该只对 @ConfigurationProperties
字段 Javadoc 使用纯文本,因为它们在被添加到 JSON 之前不会被处理。
如果您将 @ConfigurationProperties
与 record 类一起使用,则应该通过类级别的 Javadoc 标签 @param
提供 record 组件的描述(因为 record 类中没有显式的实例字段来放置常规的字段级别 Javadoc)。
注解处理器应用了许多启发式方法来从源模型中提取默认值。
只有当类型作为正在编译的源代码可用时,才能提取默认值。
当类型仅作为依赖项中的编译类可用时,将不会提取默认值。
此外,默认值必须是静态提供的。
特别是,不要引用在另一个类中定义的常量。
另外,注解处理器无法自动检测 Collections
的默认值。
对于无法检测到默认值的情况,应该提供 手动元数据。 考虑以下示例:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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
}
}
为了记录上述类中属性的默认值,您可以将以下内容添加到 模块的手动元数据中:
{"properties": [
{
"name": "my.messaging.addresses",
"defaultValue": ["a", "b"]
},
{
"name": "my.messaging.container-type",
"defaultValue": "simple"
}
]}
注意:只需要属性的 name
就可以为现有属性记录额外的元数据。
嵌套属性
注解处理器自动将内部类视为嵌套属性。
我们可以在命名空间中为 ip
和 port
创建一个子命名空间,而不是在命名空间的根目录记录它们。
考虑更新后的示例:
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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;
}
}
}
前面的示例为 my.server.name
、my.server.host.ip
和 my.server.host.port
属性生成元数据信息。
您可以在字段或 getter 方法上使用 @NestedConfigurationProperty
注解来指示应该将常规(非内部)类视为嵌套类。
提示:这对集合和映射没有影响,因为这些类型是自动识别的,并且为每个类型生成一个元数据属性。
添加额外的元数据
Spring Boot 的配置文件处理非常灵活,通常存在可能未绑定到 @ConfigurationProperties
bean 的属性。
您可能还需要调整现有键的某些属性。
为了支持这些情况并让您提供自定义"提示",注解处理器会自动将 META-INF/additional-spring-configuration-metadata.json
中的项目合并到主元数据文件中。
如果您引用已自动检测到的属性,则如果指定了描述、默认值和弃用信息,它们将被覆盖。 如果在当前模块中未识别手动属性声明,则将其添加为新属性。
additional-spring-configuration-metadata.json
文件的格式与常规 spring-configuration-metadata.json
完全相同。
额外的属性文件是可选的。
如果您没有任何额外的属性,请不要添加该文件。