使用註解處理器產生您自己的元數據

您可以使用 spring-boot-configuration-processor jar,從標註 @ConfigurationProperties 的項目輕鬆產生您自己的組態元數據檔案。此 jar 包含一個 Java 註解處理器,它會在您的專案編譯時被調用。

組態註解處理器

若要使用此處理器,請包含 spring-boot-configuration-processor 的依賴。

對於 Maven,此依賴應宣告為可選,如下列範例所示

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>

對於 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,您可以明確地組態 maven-apt-plugin,並僅在此處添加註解處理器的依賴。您也可以讓 AspectJ 外掛程式執行所有處理,並在 maven-compiler-plugin 組態中停用註解處理,如下所示

<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 屬性,以正確的順序列出註解處理器。如果您未使用此屬性,並且註解處理器由 classpath 上可用的依賴項取得,請確保在 spring-boot-configuration-processor 依賴項之前定義 lombok 依賴項。

自動元數據產生

處理器會挑選以 @ConfigurationProperties 註解的類別和方法。

不支援使用 @ConfigurationProperties 元註解的自訂註解。

如果類別具有單一參數化建構子,則會為每個建構子參數建立一個屬性,除非該建構子以 @Autowired 註解。如果類別具有以 @ConstructorBinding 明確註解的建構子,則會為該建構子的每個建構子參數建立一個屬性。否則,會透過標準 getter 和 setter 的存在來探索屬性,並針對集合和 Map 類型進行特殊處理(即使僅存在 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;
	}
	// fold:off

}

這會公開三個屬性,其中 my.server.name 沒有預設值,而 my.server.ipmy.server.port 預設值分別為 "127.0.0.1"9797。欄位上的 Javadoc 用於填入 description 屬性。例如,my.server.ip 的描述為「IP 位址以進行監聽」。

您應該僅將純文字與 @ConfigurationProperties 欄位 Javadoc 一起使用,因為它們在新增至 JSON 之前不會被處理。

如果您將 @ConfigurationProperties 與 record 類別一起使用,則應透過類別層級 Javadoc 標籤 @param 提供 record 組件的描述(在 record 類別中沒有明確的實例欄位來放置常規欄位層級 Javadoc)。

註解處理器會應用許多啟發式方法,從來源模型中提取預設值。預設值必須以靜態方式提供。特別是,不要參考在另一個類別中定義的常數。此外,註解處理器無法自動偵測 EnumCollection 的預設值。

對於無法偵測到預設值的情況,應提供手動元數據。考慮下列範例

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 即可記錄現有屬性的其他元數據。

巢狀屬性

註解處理器會自動將內部類別視為巢狀屬性。我們可以為其建立子命名空間,而不是在命名空間的根目錄記錄 ipport。考慮更新後的範例

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;
		}
		// @fold:off // getters/setters ...

	}

}

上述範例會產生 my.server.namemy.server.host.ipmy.server.host.port 屬性的元數據資訊。您可以在欄位上使用 @NestedConfigurationProperty 註解,以指示應將常規(非內部)類別視為巢狀類別。

這對集合和 Map 沒有影響,因為這些類型會自動識別,並且會為每個類型產生單一元數據屬性。

新增額外元數據

Spring Boot 的組態檔案處理非常彈性,而且通常情況下,可能存在未繫結至 @ConfigurationProperties bean 的屬性。您可能也需要調整現有金鑰的某些屬性。為了支援這些情況並讓您提供自訂「提示」,註解處理器會自動將來自 META-INF/additional-spring-configuration-metadata.json 的項目合併到主要元數據檔案中。

如果您參考已自動偵測到的屬性,則描述、預設值和棄用資訊會被覆寫(如果已指定)。如果手動屬性宣告在目前的模組中未識別,則會將其新增為新屬性。

additional-spring-configuration-metadata.json 檔案的格式與常規 spring-configuration-metadata.json 完全相同。額外的屬性檔案是可選的。如果您沒有任何額外屬性,請勿新增檔案。