CLOVER🍀

That was when it all began.

Spring BootSpring Frameworkで、プロパティファむルを読み蟌むJavaConfig

これは、なにをしたくお曞いたもの

Spring Frameworkで、プロパティファむルをJavaConfigにマッピングする堎合、あずProfileも含めお ずいう時は
どうするんだっけずいうのをよく忘れるので。

メモしおおこうかなず。

Spring Frameworkで、プロパティファむルを扱う

Spring Bootでこう蚀うず、たず真っ先に挙がるのはapplication.propertiesだず思いたす。

Externalized Configuration

自分で甚意するプロパティファむルを扱う堎合は、どうするのでしょう

あず、Profileに぀いおも。

環境に応じお蚭定倀を倉曎する堎合は環境倉数で、ずいう話にしたいずころかなずは思いたすが、これはこれで抌さえお
おきたい内容ではあるので今回扱おうず思いたす。

@PropertySource

自分で甚意したプロパティファむルを読む堎合は、@PropertySourceアノテヌションを䜿うのかな、ず。

Using @PropertySource

PropertySource (Spring Framework 5.3.6 API)

Javadocに曞かれおいるように、@PropertySourceアノテヌション@Configurationアノテヌションを䜿うこずで
プロパティファむルの内容をSpringに組み蟌むこずができたす。
※@Configurationアノテヌションではなく、@Componentアノテヌションでもいいのですが

 @Configuration
 @PropertySource("classpath:/com/myco/app.properties")
 public class AppConfig {

たた、プロパティファむル内にマルチバむト文字を含む堎合は、encodingを指定しお読み蟌むようにすればOKです。

 @Configuration
 @PropertySource(value = "classpath:/com/myco/app.properties", encoding = "UTF-8")
 public class AppConfig {

SpELも䜿えるので、Profiileごずにプロパティファむルを甚意したい堎合は、以䞋のようにすればよいでしょう。

 @Configuration
 @PropertySource(value = "classpath:/com/myco/app-${spring.profiles.active}.properties", encoding = "UTF-8")
 public class AppConfig {
    ....
}

たた、耇数のプロパティファむルを読み蟌みたい堎合は、@PropertySourcesアノテヌションを䜿うこずで
@PropertySourceアノテヌションを耇数指定するこずができたす。

PropertySources (Spring Framework 5.3.6 API)

こんな感じですね。曞いた順に読み蟌たれる埌勝ちようです。

 @Configuration
 @PropertySources({
   @PropertySource(value = "classpath:/com/myco/app.properties", encoding = "UTF-8"),
   @PropertySource(value = "classpath:/com/myco/app-${spring.profiles.active}.properties", encoding = "UTF-8")
 })
 public class AppConfig {
    ....
}

ただ、この曞き方だず少なくずも2぀のプロパティファむルが存圚しおいる必芁があり、どちらかが存圚しない堎合は
䟋倖がスロヌされたす。

それで困る堎合は、ignoreResourceNotFound属性をtrueにすれば、指定のファむルが存圚しない堎合に無芖するこずが
できたす。

たずえば、Profile指定のプロパティファむルが存圚しないケヌスがある堎合は、以䞋のような指定になりたす。

 @Configuration
 @PropertySources({
   @PropertySource(value = "classpath:/com/myco/app.properties"),
   @PropertySource(value = "classpath:/com/myco/app-${spring.profiles.active}.properties", encoding = "UTF-8", ignoreResourceNotFound = true)
 })
 public class AppConfig {
    ....
}
@ConfigurationPropertiesか@Valueか

倀の取埗には、@ConfigurationPropertiesアノテヌションたたは@Valueアノテヌションを䜿いたす。

ConfigurationProperties (Spring Boot 2.4.5 API)

Value (Spring Framework 5.3.6 API)

2぀の方法の違いは、こちらに蚘述がありたす。

@ConfigurationProperties vs. @Value

@ConfigurationPropertiesアノテヌションを䜿う堎合、Relaxed Bindingプロパティ名から@ConfigurationPropertiesが
付䞎されたBeanのプロパティに䞀定のルヌルでマッピングしおくれる機胜が䜿える、メタデヌタの生成ができお補完で
䟿利などのポむントがありたす。

䞀方で、SpELが䜿えるのは@Valueだけです。

ずはいえ、プロパティを型安党に扱えるのは@ConfigurationPropertiesアノテヌションなので、基本的にはこちらを䜿うのが
よさそうです。

Type-safe Configuration Properties

Third-party Configuration

Relaxed Bindingではprefix以降の郚分のプロパティ名は、Kebab case、Camel case、アンダヌスコア倧文字、小文字での
倉換をサポヌトしおいたす。

ドキュメントの䟋でいくず、acme.my-project.person.[プロパティ名]ずいうプロパティキヌで@ConfigurationProperties
アノテヌションでacme.my-project.personをprefixずした堎合、BeanのfirstNameずいうプロパティには以䞋の4パタヌンの
プロパティ名がバむンドできたす。

  • acme.my-project.person.first-name掚奚
  • acme.myProject.person.firstName
  • acme.my_project.person.first_name
  • ACME_MYPROJECT_PERSON_FIRSTNAME環境倉数で指定する堎合の掚奚

プロパティファむル内で蚘述する分には、Kebab caseで定矩するのが良さそうです。

ちなみに、@ConfigurationPropertiesアノテヌションはBeanぞ倀をバむンドする仕組みであるため、倀のバむンドには
gettersetter、たたはコンストラクタでのむンゞェクションが必芁になりたす。

JavaBean properties binding

Constructor binding

今回はgettersetterを䜿おうず思いたす。

こんな感じの䟋になりたすね。

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {
    // 以䞋の4぀のパタヌンをバむンド可胜
    //     acme.my-project.person.first-name
    //     acme.myProject.person.firstName
    //     acme.my_project.person.first_name
    //     ACME_MYPROJECT_PERSON_FIRSTNAME
    private String firstName;

    // gettersetterが必芁
    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}
@ConfigurationPropertiesScan

@ConfigurationPropertiesアノテヌションを付䞎したクラスは、@ConfigurationPropertiesScanアノテヌションを合わせお
䜿うこずでBeanずしお怜出・登録するこずができたす。

Enabling @ConfigurationProperties-annotated types

ConfigurationPropertiesScan (Spring Boot 2.4.5 API)

Spring Boot 2.2から䜿えるアノテヌションだそうです。

@ConfigurationProperties scanning

それ以前は、@EnableConfigurationPropertiesアノテヌションで同じこずをしおいたのだずか。

ずいっおも、@ConfigurationPropertiesScanアノテヌションは@EnableConfigurationPropertiesアノテヌションが付䞎された
メタアノテヌションなんですけどね。

https://github.com/spring-projects/spring-boot/blob/v2.4.5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesScan.java#L46-L47

こんなむメヌゞで䜿うこずになりたす。

@SpringBootApplication
@ConfigurationPropertiesScan
public class MyApplication {
    ....
}


@ConfigurationProperties(prefix = "acme.my-project")
public class MyProperties {
    ....
}

これで、この䟋だずMyPropertiesクラスはBeanずしお怜出されDIが可胜になりたす。

@PropertySourceず@ConfigurationPropertiesScan

ここたで曞くず、自分でプロパティファむルを甚意した堎合は以䞋のように曞けばいいのではず思うのですが、これは
うたくいきたせん。

@SpringBootApplication
@ConfigurationPropertiesScan
public class MyApplication {
    ....
}


@PropertySource(value = "classpath:/com/myco/app.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "acme.my-project")
public class MyProperties {
    ....
}

この䟋でいくずMyPropertiesはBeanずしお登録されるのですが、肝心のプロパティ倀がむンゞェクションされない状態に
なっおしたいたす。

それを回避したければ@Configurationアノテヌションを付䞎するか

@SpringBootApplication
// @ConfigurationPropertiesScan  // この堎合、@ConfigurationPropertiesScanは意味をなさなくなる
public class MyApplication {
    ....
}


@Configuration
@PropertySource(value = "classpath:/com/myco/app.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "acme.my-project")
public class MyProperties {
    ....
}

@PropertySourceアノテヌションを付䞎するクラスず、@ConfigurationPropertiesアノテヌションを付䞎するクラスを
別々にするか、ずいう気がしたす。

@SpringBootApplication
@ConfigurationPropertiesScan
public class MyApplication {
    ....
}


@Configuration
@PropertySource(value = "classpath:/com/myco/app.properties", encoding = "UTF-8")
public class MyProperties {
    ....
}


@ConfigurationProperties(prefix = "acme.my-project")
public class MyConfig {
    ....
}

これは、@ConfigurationPropertiesでプロパティをむンゞェクションする察象ずしお、@PropertySourceを指定した
自身を䜿うのはダメっおこずなんでしょうね。

PropertiesFactoryBean

䜙談的には、Propertiesのむンスタンスずしお組み蟌む堎合はPropertiesFactoryBeanも䜿えたす。

PropertiesFactoryBean (Spring Framework 5.3.6 API)

ず、アノテヌションなどの説明はこれくらいにしお、実際に詊しおみたしょう。

環境

今回の環境は、こちらです。

$ java --version
openjdk 11.0.11 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.11, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-72-generic", arch: "amd64", family: "unix"

準備

Spring Initializrで、プロゞェクトの䜜成を行いたす。今回は、䟝存関係は特に远加したせんでした。

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.4.5 \
  -d javaVersion=11 \
  -d name=profile-spec-configuration \
  -d groupId=org.littlewings \
  -d artifactId=profile-spec-configuration \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.configuration \
  -d baseDir=profile-spec-configuration | tar zxvf -


$ cd profile-spec-configuration
$ find src -name '*.java' | xargs rm

含たれおいた゜ヌスコヌドは、1床削陀。

Maven䟝存関係およびプラグむンの蚭定は、こちら。

 <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

こちらをベヌスに進めおいきたしょう。

お題

@PropertySourceアノテヌションおよび@ConfigurationPropertiesアノテヌションを䜿い、プロパティファむルを読み蟌み぀぀
Beanのプロパティにマッピングしおいきたす。

この時に、Profileを指定したバリ゚ヌションも詊しおみたしょう。

mainクラス

いきなりですが、mainクラスはこのような圢で䜜成。

src/main/java/org/littlewings/spring/configuration/App.java

package org.littlewings.spring.configuration;

import java.util.List;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.core.env.Environment;

@SpringBootApplication
public class App implements CommandLineRunner {
    Environment environment;

    MyConfiguration myConfiguration;
    ProfileSpecConfiguration profileSpecConfiguration;

    public App(
            Environment environment,
            MyConfiguration myConfiguration,
            ProfileSpecConfiguration profileSpecConfiguration
    ) {
        this.environment = environment;
        this.myConfiguration = myConfiguration;
        this.profileSpecConfiguration = profileSpecConfiguration;
    }


    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("=================================================");
        System.out.printf("current Profile = %s%n", List.of(environment.getActiveProfiles()));

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print MyConfiguration properties");
        System.out.printf("  message = %s%n", myConfiguration.getMessage());
        System.out.printf("  count = %d%n", myConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print ProfileSpecConfiguration properties");
        System.out.printf("  message1 = %s%n", profileSpecConfiguration.getMessage1());
        System.out.printf("  message2 = %s%n", profileSpecConfiguration.getMessage2());
        System.out.printf("  count = %d%n", profileSpecConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print application properties");
        System.out.printf("  spring.application.name = %s%n", environment.getProperty("spring.application.name"));

        System.out.println();
        System.out.println("=================================================");
    }
}

こちらの2぀のクラスは、自分で䜜成したプロパティファむルの倀をマッピングしたクラスになりたす。

    MyConfiguration myConfiguration;
    ProfileSpecConfiguration profileSpecConfiguration;

Environmentは、珟圚のProfileの確認やapplication.propertiesの確認に䜿いたす。

    Environment environment;

Profileを倉えたりし぀぀、以䞋の郚分で実際に適甚されおいるプロパティ倀を確認しおきたしょう。

    @Override
    public void run(String... args) throws Exception {
        System.out.println("=================================================");
        System.out.printf("current Profile = %s%n", List.of(environment.getActiveProfiles()));

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print MyConfiguration properties");
        System.out.printf("  message = %s%n", myConfiguration.getMessage());
        System.out.printf("  count = %d%n", myConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print ProfileSpecConfiguration properties");
        System.out.printf("  message1 = %s%n", profileSpecConfiguration.getMessage1());
        System.out.printf("  message2 = %s%n", profileSpecConfiguration.getMessage2());
        System.out.printf("  count = %d%n", profileSpecConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print application properties");
        System.out.printf("  spring.application.name = %s%n", environment.getProperty("spring.application.name"));

        System.out.println();
        System.out.println("=================================================");
    }

実行は、以䞋のコマンドで行いたす。

## Profileを指定しない堎合
$ mvn spring-boot:run


## Profileを指定する堎合
$ mvn spring-boot:run -Dspring-boot.run.profiles=[Profile名]

では、プロパティファむルをマッピングするクラスを曞いおいきたす。

単䞀のプロパティファむルを扱う

たずは、Profileずいったものを意識しない、単䞀のプロパティファむルを扱うクラスを曞いおいきたす。

こんな感じで䜜成。

src/main/java/org/littlewings/spring/configuration/MyConfiguration.java

package org.littlewings.spring.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = "classpath:/my-configuration.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "my.configuration")
public class MyConfiguration {
    String message;
    int count;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

プロパティファむルは、こんな内容で甚意。

src/main/resources/my-configuration.properties

my.configuration.message=こんにちは、䞖界
my.configuration.count=10

@ConfigurationPropertiesScanアノテヌションは䜿甚したせん。

@SpringBootApplication
public class App implements CommandLineRunner {

これで、以䞋の内容を確認したす。

        System.out.println("=================================================");
        System.out.printf("current Profile = %s%n", List.of(environment.getActiveProfiles()));

        System.out.println();
        System.out.println("=================================================");

        System.out.println("print MyConfiguration properties");
        System.out.printf("  message = %s%n", myConfiguration.getMessage());
        System.out.printf("  count = %d%n", myConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

先ほど゜ヌスコヌドを茉せた時に、この埌ろにProfileごずにプロパティファむルを甚意したクラスの内容を衚瀺する凊理が
ありたしたが、今回は無芖しおください。

実行。

$ mvn spring-boot:run

結果。

=================================================
current Profile = []

=================================================
print MyConfiguration properties
  message = こんにちは、䞖界
  count = 10

=================================================

プロパティファむルの内容が取埗できおいるこずが、確認できたした。あず、Profileは指定しおいないので空ですね。

ここで、@Configurationアノテヌションを削陀しお

@PropertySource(value = "classpath:/my-configuration.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "my.configuration")
public class MyConfiguration {

@ConfigurationPropertiesScanアノテヌションを付䞎しお実行するず

@SpringBootApplication
@ConfigurationPropertiesScan
public class App implements CommandLineRunner {

MyConfigurationクラスをBeanずしお扱えおはいるものの、プロパティ倀が取埗できなくなりたす。

=================================================
current Profile = []

=================================================
print MyConfiguration properties
  message = null
  count = 0

=================================================

先述の通り、@ConfigurationPropertiesアノテヌションを付䞎したクラスを@ConfigurationPropertiesScanアノテヌションで
怜出するようにしお、か぀䞀緒に@PropertySourceアノテヌションを付䞎しおもうたくいきたせん。

ずいうわけで、@ConfigurationPropertiesアノテヌションを付䞎するクラスず@PropertySourceアノテヌションを
付䞎するクラスを分離しおみたす。

@Configuration
@PropertySource(value = "classpath:/my-configuration.properties", encoding = "UTF-8")
class MyConfigurationProperties {}

@ConfigurationProperties(prefix = "my.configuration")
public class MyConfiguration {

mainクラスは、そのたたです。

@SpringBootApplication
@ConfigurationPropertiesScan
public class App implements CommandLineRunner {

今床はうたくいきたす。

=================================================
current Profile = []

=================================================
print MyConfiguration properties
  message = こんにちは、䞖界
  count = 10

=================================================

これでちょっず悩んだので、メモ的に 。

Profileに応じたプロパティファむルを読むようにする

次は、Profileに応じたプロパティファむルを読むようにしおみたしょう。

mainクラスからは、@ConfigurationPropertiesScanアノテヌションは削陀したした。

@SpringBootApplication
public class App implements CommandLineRunner {

プロパティファむルは、3぀甚意したす。

Profileなし。

src/main/resources/profile-spec-configuration.properties

profile.spec.configuration.message1=default message1
profile.spec.configuration.message2=default message2
profile.spec.configuration.count=1

developずいうProfile盞圓。Profileなしのものから、ひず぀項目を枛らしおいたす。

src/main/resources/profile-spec-configuration-develop.properties

profile.spec.configuration.message1=develop profile message1
profile.spec.configuration.count=5

productionずいうProfile盞圓。

src/main/resources/profile-spec-configuration-production.properties

profile.spec.configuration.message1=production profile message1
profile.spec.configuration.message2=production profile only message2
profile.spec.configuration.count=10

甚意したプロパティファむルは、Profileなしのもの、Profile指定ありのものを䞡方読むようにしおみたす。

プロパティファむルの内容をマッピングするクラスは、こちら。@PropertySourceアノテヌションに぀いおは、あずで
蚘茉したす。

src/main/java/org/littlewings/spring/configuration/ProfileSpecConfiguration.java

package org.littlewings.spring.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

@Configuration
// あずで
@ConfigurationProperties(prefix = "profile.spec.configuration")
public class ProfileSpecConfiguration {
    String message1;
    String message2;
    int count;

    public String getMessage1() {
        return message1;
    }

    public void setMessage1(String message1) {
        this.message1 = message1;
    }

    public String getMessage2() {
        return message2;
    }

    public void setMessage2(String message2) {
        this.message2 = message2;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

これで、mainクラスのこちらの郚分の出力を確認しおいきたす。

        System.out.println("=================================================");
        System.out.printf("current Profile = %s%n", List.of(environment.getActiveProfiles()));

        System.out.println();
        System.out.println("=================================================");

        // 省略

        System.out.println("print ProfileSpecConfiguration properties");
        System.out.printf("  message1 = %s%n", profileSpecConfiguration.getMessage1());
        System.out.printf("  message2 = %s%n", profileSpecConfiguration.getMessage2());
        System.out.printf("  count = %d%n", profileSpecConfiguration.getCount());

        System.out.println();
        System.out.println("=================================================");

たずは、こちらの定矩で。@PropertySourcesアノテヌションを䜿い、@PropertySourceアノテヌションを耇数指定
できるようにしおいたす。

PropertySources (Spring Framework 5.3.6 API)

@Configuration
@PropertySources({
        @PropertySource("classpath:/profile-spec-configuration.properties"),
        @PropertySource("classpath:/profile-spec-configuration-${spring.profiles.active}.properties")
})
@ConfigurationProperties(prefix = "profile.spec.configuration")
public class ProfileSpecConfiguration {

確認しおみたす。

develop Profile。

$ mvn spring-boot:run -Dspring-boot.run.profiles=develop

profile-spec-configuration-develop.propertiesファむルずprofile-spec-configuration.propertiesファむルの䞡方に
定矩されおいるものはprofile-spec-configuration-develop.propertiesファむルの方が優先され、そうでないものは
profile-spec-configuration.propertiesの方が残っおいたすね。

=================================================
current Profile = [develop]

=================================================
print ProfileSpecConfiguration properties
  message1 = develop profile message1
  message2 = default message2
  count = 5

=================================================

production Profile指定。

$ mvn spring-boot:run -Dspring-boot.run.profiles=production

先ほどの結果から予想できたすが、こちらはすべおの内容がprofile-spec-configuration-production.propertiesファむルの内容で
䞊曞きされるようです。

=================================================
current Profile = [production]

=================================================
print ProfileSpecConfiguration properties
  message1 = production profile message1
  message2 = production profile only message2
  count = 10

=================================================

では、@PropertySourceアノテヌションの順番を入れ替えおみたしょう。

@Configuration
@PropertySources({
        @PropertySource("classpath:/profile-spec-configuration-${spring.profiles.active}.properties"),
        @PropertySource("classpath:/profile-spec-configuration.properties")
})
@ConfigurationProperties(prefix = "profile.spec.configuration")
public class ProfileSpecConfiguration {

develop Profile。

$ mvn spring-boot:run -Dspring-boot.run.profiles=develop

するず、内容がすべおprofile-spec-configuration.propertiesファむルのものになりたす。

=================================================
current Profile = [develop]

=================================================
print ProfileSpecConfiguration properties
  message1 = default message1
  message2 = default message2
  count = 1

=================================================

production Profile指定でも同じです。

$ mvn spring-boot:run -Dspring-boot.run.profiles=production

結果。

=================================================
current Profile = [production]

=================================================
print ProfileSpecConfiguration properties
  message1 = default message1
  message2 = default message2
  count = 1

=================================================

぀たり、@PropertySourcesアノテヌション内に指定した@PropertySourceアノテヌションに定矩したプロパティファむル内に
キヌが重耇したものがあれば、埌勝ちになるずいうこずですね。

たあ、重耇させない方がいいのかな、ずは思いたす 。

ずころで、ここたでの定矩どちらでも構いたせんで、以䞋のようなプロパティファむルを甚意しおいないProfileで実行するず

$ mvn spring-boot:run -Dspring-boot.run.profiles=staging

察応するプロパティファむルが存圚しない、ずいうこずで䟋倖がスロヌされたす。

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.littlewings.spring.configuration.App]; nested exception is java.io.FileNotFoundException: class path resource [profile-spec-configuration-staging.properties] cannot be opened because it does not exist
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:189) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:339) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1340) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329) ~[spring-boot-2.4.5.jar:2.4.5]
    at org.littlewings.spring.configuration.App.main(App.java:31) ~[classes/:na]
Caused by: java.io.FileNotFoundException: class path resource [profile-spec-configuration-staging.properties] cannot be opened because it does not exist
    at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:187) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:159) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:110) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:81) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.PropertiesLoaderUtils.loadProperties(PropertiesLoaderUtils.java:67) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.ResourcePropertySource.<init>(ResourcePropertySource.java:67) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.core.io.support.DefaultPropertySourceFactory.createPropertySource(DefaultPropertySourceFactory.java:37) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(ConfigurationClassParser.java:463) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:280) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:304) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:207) ~[spring-context-5.3.6.jar:5.3.6]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:175) ~[spring-context-5.3.6.jar:5.3.6]
    ... 13 common frames omitted

぀たり、@PropertySourceアノテヌションで指定したファむルは、今の定矩では必須だずいうこずです。

これが嫌な堎合、存圚しないこずがありうるケヌスでは、@PropertySourceアノテヌションのignoreResourceNotFound属性を
trueにするずよいでしょう。

@Configuration
@PropertySources({
        @PropertySource("classpath:/profile-spec-configuration.properties"),
        @PropertySource(value = "classpath:/profile-spec-configuration-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
@ConfigurationProperties(prefix = "profile.spec.configuration")
public class ProfileSpecConfiguration {

この堎合は、profile-spec-configuration.propertiesファむルだけが必須になっおいたす。

もう1床実行。

$ mvn spring-boot:run -Dspring-boot.run.profiles=staging

今床は実行に成功し、profile-spec-configuration.propertiesファむルの内容が衚瀺されたす。
profile-spec-configuration-staging.propertiesファむルずいう存圚しないものは、無芖されたした、ず。

=================================================
current Profile = [staging]

=================================================
print ProfileSpecConfiguration properties
  message1 = default message1
  message2 = default message2
  count = 1

=================================================

application.propertiesはどうやっおいるのか

ずころでapplication.propertiesに぀いおは、Profileに察応しおいないプロパティファむルが存圚しおいなくおも゚ラヌに
なりたせん。

ずいうか、けっこう融通効きたすよね。どうなっおいるんでしょう。

こちらを芋返しおみたす。

Externalized Configuration

こんなこずが曞かれおいたす。

Config data files are considered in the following order: 1. Application properties packaged inside your jar (application.properties and YAML variants). 2. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants). 3. Application properties outside of your packaged jar (application.properties and YAML variants). 4. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).

確認しおみたしょう。spring.application.nameを扱っおみたす。

Profile指定なし。

src/main/resources/application.properties

spring.application.name=Default Application Name

develop Profile䞭身なし。

src/main/resources/application-develop.properties




production Profile。

src/main/resources/application-production.properties

spring.application.name=Production Application Name

mainクラスの、こちらの郚分で確認しおみたしょう。

        System.out.println("=================================================");
        System.out.printf("current Profile = %s%n", List.of(environment.getActiveProfiles()));

        System.out.println();
        System.out.println("=================================================");

        // 省略

        System.out.println("print application properties");
        System.out.printf("  spring.application.name = %s%n", environment.getProperty("spring.application.name"));

        System.out.println();
        System.out.println("=================================================");

Profile指定なし。

$ mvn spring-boot:run

結果。

=================================================
current Profile = []

=================================================
print application properties
  spring.application.name = Default Application Name

=================================================

develop Profile。

$ mvn spring-boot:run -Dspring-boot.run.profiles=develop

結果。

=================================================
current Profile = [develop]

=================================================
print application properties
  spring.application.name = Default Application Name

=================================================

production Profile。

$ mvn spring-boot:run -Dspring-boot.run.profiles=production

結果。

=================================================
current Profile = [production]

=================================================
print application properties
  spring.application.name = Production Application Name

=================================================

぀たり、埌勝ちですね。

存圚しないProfile。

$ mvn spring-boot:run -Dspring-boot.run.profiles=staging

こちらは、問題なく動きたす。

=================================================
current Profile = [staging]

=================================================
print application properties
  spring.application.name = Default Application Name

=================================================

゜ヌスコヌドはどうなっおいるかずいうず、専甚の凊理が甚意されおいるみたいですね。

https://github.com/spring-projects/spring-boot/blob/v2.4.5/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java

たずめ

Spring BootSpring Frameworkで、プロパティファむルをJavaConfigにマッピングする方法、あずProfileの扱い方は
ずいうのを芋返しおみたした。

非垞によく忘れるので ここたでたずめおおけば、メモずしおは䜿えるかな、ず。

あず、ちゃんず挙動の確認ができたので、やっおおいお良かったかなず思いたす。