なかなかコレ!というサンプルが見つからなかったのでメモ。
やりたかったこと
マルチプロジェクトの構成で、共通の部品を集めたプロジェクト側にもプロパティファイルを置きたかったが、application.yml だと利用側のプロパティファイルと名前がバッティングするため別名にする必要があった。
- SpringBoot に
application-common.yml
のような名前の yaml のプロパティファイルを読み込ませる - その際、
application-common-test.yml
のようにプロファイル名をくっつけたファイルも読み込ませる - 読み込んだプロパティは Configuration クラスにマッピングする
実現方法
ポイント
- Configuration クラスで
@PropertySource
を使用して読み込むプロパティファイルを指定する -
@PropertySource
は yaml ファイルの読み込みに対応していないので読み込み用のクラスを作成して読み込めるようにする
ソースコード
完全なソースコードはこちら
Configuration クラス
@PropertySource
の value
で ファイル名を、factory
で Yaml 読み込み用のクラスを指定する。
FooConfig.java
@Configuration
@ConfigurationProperties(prefix = "foo")
@Component
@PropertySource(value = {"classpath:/foo-config.yml",
"classpath:/foo-config-${spring.profiles.active}.yml"},
factory = YamlPropertySourceFactory.class)
@Data
public class FooConfig {
private BarConfig bar;
private BazConfig baz;
@Data
public static class BarConfig {
private String setting1;
}
@Data
public static class BazConfig {
private String setting1;
private String setting2;
}
}
Factory クラス
どこかから拝借したソース。
Spring のYamlPropertiesFactoryBean
を使用して読み込んだ Yaml を Properties
に変換し、PropertySource
にして返しているよう。
YamlPropertySourceFactory.java
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource)
throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private static Properties loadYamlIntoProperties(EncodedResource resource)
throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException) {
throw (FileNotFoundException) e.getCause();
}
throw e;
}
}
}
動作確認
SpringPropertySourceTest.java
@SpringBootTest
@ActiveProfiles("test")
public class SpringPropertySourceTest {
@Autowired
FooConfig fooConfig;
@Test
public void test() {
assertThat(fooConfig.getBar().getSetting1()).isEqualTo("barbar1");
assertThat(fooConfig.getBaz().getSetting2()).isEqualTo("bazbaz2");
}
}
おまけ
SpringBoot2.3.0 以降だと@PropertySource の ${spring.profiles.active}
を解決できずにエラーが出ます。別記事に書いておきました。(これのせいでめっちゃ時間食った・・・)
SpringBoot2.3.0 以降で@PropertySource の value に使用しているプレースホルダの解決に失敗する