この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。齋藤です。
今回は久しぶりにSpringです。 色々リファクタリングをしている時にふとSpringのソースを覗いてみたところ 楽しそうな内容が書けそうだったので書きました。
今回の記事は次のようなケースで使えるでしょう。
- ConfigurationPropertiesのクラスをBeanとして複数登録させたい
- ConfigurationPropertiesのクラスに
@Component
を付与したくない - ConfigurationPropertiesのクラスのprefixを利用場所で変えたい
はじめに
今、自分たちがやっているプロジェクトでは次のようなクラスを作ることがあります。
application.properties
を型安全に取り扱う仕組みですね。
@ConfigurationProperties("config.hoge")
@Component
public class HogeConfig {
String name;
String test;
// getter, setter, equals, hashCode 省略
}
先ほども書きましたが、次のようなことをしたいケースがあるかもしれません。(ないかもしれません。いや、多分ない。)
- ConfigurationPropertiesのクラスをBeanとして複数登録させたい
- ConfigurationPropertiesのクラスに
@Component
を付与したくない - ConfigurationPropertiesのクラスのprefixを利用場所で変えたい
今回の記事では、次のような SampleConfig
クラスを例に記事を書いていきます。
先ほど挙げたコードから、@Component
や @ConfigurationProperties
のアノテーションをほぼ外しただけです。
public class SampleConfig {
String name;
String test;
// getter, setter, equals, hashCode 省略
}
どうやって複数のprefixでBeanを登録するのか
では、やっていきますが ConfigurationPropertiesのクラスを普通にBean登録するだけです。 これだけです。
@Configuration
public class SampleConfiguration {
@Bean
@ConfigurationProperties(prefix = "config.hoge")
SampleConfig configHoge() {
return new SampleConfig();
}
@Bean
@ConfigurationProperties(prefix = "config.fuga")
SampleConfig configFuga() {
return new SampleConfig();
}
}
多分、パッとみたら大体わかるでしょう。
config.hoge配下の設定を読み取るconfigHoge
というBeanと
config.fuga配下の設定を読み取るconfigFuga
というBeanが登録されています。
もちろん、application.properties
などに書かれた設定が読み込まれます。
なぜ先ほどのコードが動くのか
その秘密は ConfigurationPropertiesBindingPostProcessor
にあります。
Springにはそもそも、BeanPostProcessorという仕組みがあり Beanの登録時に何かをやりたい仕組みが存在します。
BeanPostProcessorは次のようなインターフェースです。 beanとそのbeanNameが渡されたら、beanを戻り値とするようなメソッドを持つインターフェースです。
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
ConfigurationPropertiesアノテーションに関わるBeanPostProcessorは
ConfigurationPropertiesBindingPostProcessor
です。
org.springframework.boot.context.properties
パッケージに所属しています。
さて、ここで、先ほどの ConfigurationPropertiesBindingPostProcessor
の postProcessBeforeInitialization
を覗いてみます。
次のようなコードが書かれています。
// 省略
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// クラスからアノテーション探す
ConfigurationProperties annotation = AnnotationUtils
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
// ファクトリメソッドからアノテーションを探す ⇨ この記事ではConfigurationクラスのメソッド
annotation = this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
return bean;
}
// 省略
なるほど、なんとなくわかってきたでしょうか。
クラスそのものにアノテーションが付いているか、もしくはファクトリメソッドに ConfigurationProperties
アノテーションが付いていれば
postProcessBeforeInitialization
メソッドが呼ばれるようになっています。
postProcessBeforeInitialization
メソッドを見ると
ConversionServiceだったり、Validatorだったりの単語が踊っています。
今回は解説しませんが、このメソッドの中で application.properties
などから読み込んだ設定値を
ConfigurationPropertiesのアノテーションが付与されたBeanに値をセットしています。
[追記] DIして使う際には注意
簡単にですが、テストコードで使う場合の注意点を示しておきます。 ConfigurationPropertiesもただのBeanですので、 Dependency Injectionする場合には注意が必要です。
具体的には以下のようなコードなら動作します。(テストコードから抜粋) 以下のように、QualifierでBean名を指定したりする必要があります。
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConfigpropsApplicationTests {
@Autowired
@Qualifier("configHoge")
SampleConfig configHoge;
@Autowired
@Qualifier("configFuga")
SampleConfig configFuga;
}
もしくは次のようなCustom Qualifier Annotationを使って使って見るのも良いでしょう。
// Hogeアノテーション
@Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.TYPE,
ElementType.TYPE_PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Hoge {
}
// Fugaアノテーション
@Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.TYPE,
ElementType.TYPE_PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fuga {
}
// Configurationクラス
@Configuration
public class QualifierSampleConfiguration {
@Bean
@Hoge
@ConfigurationProperties("config.hoge")
SampleConfig qualifierConfigHoge() {
return new SampleConfig();
}
@Bean
@Fuga
@ConfigurationProperties("config.fuga")
SampleConfig qualifierConfigFuga() {
return new SampleConfig();
}
@Bean
@ConfigurationProperties("config.default")
@Primary // PrimaryアノテーションでデフォルトのBeanを設定
SampleConfig config() {
return new SampleConfig();
}
}
// 利用の仕方
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConfigpropsApplicationTests {
@Autowired
SampleConfig defaultConfig;
@Autowired
@Fuga
SampleConfig qualifierConfigHoge;
@Autowired
@Hoge
SampleConfig qualifierConfigFuga;
}
まとめ
いかがだったでしょうか。今回は ConfigurationProperties
アノテーションの混み入った使い方を紹介しました。
今回の記事で追った内容を踏まえると、ConfigurationPropertiesもただのSpringで言うところのBeanといったところでしょうか。
機会があれば有効活用してみてください!
使う際には一癖ありますが、ハマれば強力かもしれません。
今回書いたコードはGitHubに置いてあります。