アノテーションプロセッサーを使用して独自のメタデータを生成する

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 を使用している場合は、アノテーションプロセッサーが 1 回だけ実行されることを確認する必要があります。これを行うにはいくつかの方法があります。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 属性を使用して、アノテーションプロセッサーを正しい順序で一覧表示できます。この属性を使用しておらず、アノテーションプロセッサーがクラスパスで使用可能な依存関係によって取得される場合は、spring-boot-configuration-processor 依存関係の前に lombok 依存関係が定義されていることを確認してください。

自動メタデータ生成

プロセッサーは、@ConfigurationProperties でアノテーションが付けられたクラスとメソッドの両方を取得します。

クラスにパラメーター化されたコンストラクターが 1 つある場合、コンストラクターに @Autowired アノテーションが付けられていない限り、コンストラクターパラメーターごとに 1 つのプロパティが作成されます。クラスに @ConstructorBinding で明示的にアノテーションが付けられたコンストラクターがある場合、そのコンストラクターのコンストラクターパラメーターごとに 1 つのプロパティが作成されます。それ以外の場合、プロパティは、コレクションおよびマップ型の特別な処理を備えた標準の 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;
	}
	// fold:off

}

これにより、my.server.name にはデフォルトがなく、my.server.ip および my.server.port のデフォルトはそれぞれ "127.0.0.1" および 9797 の 3 つのプロパティが公開されます。フィールドの Javadoc は、description 属性を設定するために使用されます。たとえば、my.server.ip の記述は「リッスンする IP アドレス」です。

それらは JSON に追加される前に処理されないため、@ConfigurationProperties フィールドの Javadoc でプレーンテキストのみを使用する必要があります。

レコードクラスで @ConfigurationProperties を使用する場合、レコードコンポーネントの説明はクラスレベルの Javadoc タグ @param を介して提供される必要があります (レコードクラスには、通常のフィールドレベルの Javadoc を配置するための明示的なインスタンスフィールドはありません)。

アノテーションプロセッサーは、いくつかのヒューリスティックを適用して、ソースモデルからデフォルト値を抽出します。デフォルト値は静的に提供する必要があります。特に、別のクラスで定義されている定数を参照しないでください。また、アノテーションプロセッサーは Enum および 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;
		}
		// @fold:off // getters/setters ...

	}

}

上記の例では、my.server.namemy.server.host.ipmy.server.host.port プロパティのメタデータ情報を生成します。フィールドで @NestedConfigurationProperty アノテーションを使用して、通常の(非内部)クラスをネストされているかのように扱う必要があることを示すことができます。

これらの型は自動的に識別され、それぞれに対して単一のメタデータプロパティが生成されるため、これはコレクションやマップには影響しません。

メタデータを追加する

Spring Boot の構成ファイルの処理は非常に柔軟であり、@ConfigurationProperties Bean にバインドされていないプロパティが存在する場合がよくあります。また、既存のキーの一部の属性を調整する必要がある場合があります。このような場合をサポートし、カスタムの「ヒント」を提供できるようにするために、アノテーションプロセッサーは META-INF/additional-spring-configuration-metadata.json のアイテムをメインメタデータファイルに自動的にマージします。

自動的に検出されたプロパティを参照する場合、説明、デフォルト値、非推奨情報は、指定されていれば上書きされます。手動プロパティ宣言が現在のモジュールで識別されない場合、新しいプロパティとして追加されます。

additional-spring-configuration-metadata.json ファイルの形式は、通常の spring-configuration-metadata.json とまったく同じです。追加のプロパティファイルはオプションです。追加のプロパティがない場合は、ファイルを追加しないでください。