使用註解處理器生成自己的元資料
您可以使用 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 地址。"。description
屬性只能在型別作為正在編譯的原始碼可用時填充。當型別僅作為依賴項中的已編譯類可用時,不會填充。對於這種情況,應提供手動元資料。
在 @ConfigurationProperties 欄位 Javadoc 中,您應該只使用純文字,因為在新增到 JSON 之前不會對其進行處理。 |
如果您將 @ConfigurationProperties
與 record class 一起使用,則 record 元件的描述應透過類級別的 Javadoc 標籤 @param
提供(record class 中沒有顯式的例項欄位來放置常規的欄位級 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
完全相同。額外屬性檔案是可選的。如果您沒有任何額外屬性,則無需新增此檔案。