application.properties
やapplication.yml
に設定したプロパティ値は@org.springframework.beans.factory.annotation.Value
アノテーションでインジェクション可能です。
Spring Bootではプロパティを用意して実行時に外部から変更したり、 プロファイルによって値を変えるということが多いためプロパティの管理は重要です。
次のプロパティファイル(application.properties
)を例に説明します。
target.host=api.example.com
target.port=8080
target.host
はString
でtarget.port
はint
を想定しており、 通常は次のようにインジェクションします。
@Component
public class HelloService {
@Value("${target.host}")
String host;
@Value("${target.port}")
int port;
// ...
public String hello() {
String target = "http://" + host + ":" + port;
// ...
}
}
この方法でも大きな問題はありませんが、プロパティを使う側が毎度
- プロパティ名を文字列を指定
- プロパティ値の型を指定
する必要があります。 場合によってはプロパティを提供した側の意図とは異なった形で使われる可能性があります。
@ConfigurationPropertiesによるプロパティの設定
プロパティを多用するSpring Bootでは、安全にプロパティを扱うための仕組みとして @org.springframework.boot.context.properties.ConfigurationProperties
アノテーションが用意されています。
次のようにプロパティファイルのプロパティに対応するJava BeanをDIコンテナに登録し、 @ConfigurationProperties
アノテーションをつけることで、プロパティ値が各フィールドにインジェクションされます。 prefix
属性をつけることで、プロパティ値のプリフィクスを設定することもできます。
import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
@ConfigurationProperties(prefix = "target")
public class TargetProperties {
private String host;
private int port;
// Setter/Getterは略すが、必要である
}
プロパティを使う側は次のようにTargetProperties
クラスをインジェクションして、 各プロパティのGetterを呼べば良いです。
@Component
public class HelloService {
@Autowired
TargetProperties targetProperties;
// ...
public String hello() {
String target = "http://" + targetProperties.getHost() + ":" + targetProperties.getPort();
// ...
}
}
これにより、前述の課題が解消されます。
note
@ConfigurationProperties
は正確に言うと、プロパティクラスを定義するアノテーションではなく、 DIコンテナに登録されたBeanに対してプロパティを設定するためのアノテーションです。 この例だと、@Component
をつけてDIコンテナに登録されたtargetProperties
というBeanのhost
プロパティに対してプロパティファイル中のtarget.host
の値を設定し、port
プロパティに対してプロパティファイル中のtarget.port
の値を設定しています。 インジェクション対象のBeanにSetter/Getterが必要なのはこのためです。
@ConfigurationProperties
はインジェクション対象のクラスにつけなくても、次のようにJava Configの@Bean
メソッドに つけることもできます。
@Bean @ConfigurationProperties(prefix = "target") public TargetProperties targetProperties() { return new TargetProperties(); }
Bean Validationによるプロパティ値のチェック
プロパティにはBean Validationのアノテーションで制約を設定することができます。
@Component
@ConfigurationProperties(prefix = "target")
public class TargetProperties {
@NotEmpty
private String host;
@Min(1)
@Max(65535)
private int port;
// Setter/Getterは略すが、必要である
}
制約に違反するプロパティ値が設定されている場合は起動時(DIコンテナ登録時)にorg.springframework.validation.BindException
がスローされます。
IDEによるプロパティの補完
@ConfigurationProperties
を用いて定義したプロパティはIDE(STS or IntelliJ IDEA)で補完が効きます。 ただし、補完させるためにプロパティのメタ情報を生成する必要があります。
メタ情報が存在しない場合は、次の図のように警告が出ます。
警告マークをクリックし「Add spring-boot-configuration-processor to pom.xml」をクリックすると、 プロジェクトのpom.xmlに次の依存関係が追加されます。(もちろん手動で追加しても良いです)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
note
spring-boot-configuration-processor
にはPluggable Annotation Processing API (JSR 269)を実装したクラスが含まれており、@ConfigurationProperties
アノテーションが追加Javaソースからメタ情報のJSONを出力します。IDEでビルドすると
target/META-INF/spring-configuration-metadata.json
に以下の内容のJSONファイルが出力されます。
{ "groups": [{ "name": "target", "type": "com.example.TargetProperties", "sourceType": "com.example.TargetProperties" }], "properties": [ { "name": "target.host", "type": "java.lang.String", "sourceType": "com.example.TargetProperties" }, { "name": "target.port", "type": "java.lang.Integer", "sourceType": "com.example.TargetProperties" } ], "hints": [] }
Mavenでビルド(
mvn compile
)してもspring-configuration-metadata.json
は生成されます。
メタ情報が存在する場合は、application.properties
でCtrl
+ Space
を押すと次のように、 候補が表示されます。
Spring Bootでプロパティを外部化する場合は積極的に@ConfigurationProperties
を使用するのが良いでしょう。