Spring Boot 외부 설정

외부 설정(Externalized Configuration) 이용에 대해 설명한다.

프로퍼티 파일(properties)

프로퍼티 파일 사용

기본

폴더 구성

|-build.gradle
`-src/main/
  |-java/sample/springboot/
  |  `-Main.java
  `-resources/
    `-application.properties

Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${sample.value}")
    private String value;

    public void hello() {
        System.out.println("sample.value = " + this.value);
    }
}

실행 결과

sample.value = Hello Properties File!!
  • 클래스 경로 아래에 application.properties라는 속성 파일을 배치한다.
  • 그러면 Spring Boot가 자동으로 해당 파일을 읽어 준다.
  • 속성 파일의 값은 @Value 애노테이션을 사용하여 Bean에 주입 할 수 있다.
  • ${프로퍼티 이름} 형식으로 취득하고자 하는 값을 지정한다.
파일을 두는 위치

속성 파일의 두는 곳은 몇 가지 읽기의 우선 순위가 존재한다.

  1. 시작할 때 --spring.config.location에서 지정한 파일.
  2. 현재 디렉터리 바로 아래의 config 디렉터리에 있는 파일.
  3. 현재 디렉터리에있는 파일.
  4. 클래스 경로 바로 아래의 config 패키지에 있는 파일.
  5. 클래스 경로 바로 아래에 있는 파일.

숫자가 낮을 수록 우선 순위가 높다.
우선 순위가 낮은 설정은 상위의 설정을 덮어 쓴다.

폴더 구성(jar내부)

|-application.properties
|-config/
|  `-application.properties
`-sample/springboot/
   `-Main.class

폴더 구성(실행할 때)

|-application.properties
|-other.properties
|-config/
| `-application.properties
`-build/libs/
  `-spring-boot-sample.jar

other.properties

value5=other

application.properties(현재 디렉터리의 config 디렉터리 아래)

value4=currentdir/config
value5=currentdir/config

application.properties(현재 디렉터리 아래)

value3=currentdir/
value4=currentdir/
value5=currentdir/

application.properties(클래스 경로의 config 패키지 아래)

value2=classpath/config
value3=classpath/config
value4=classpath/config
value5=classpath/config

application.properties(클래스 경로 바로 아래)

value1=classpath/
value2=classpath/
value3=classpath/
value4=classpath/
value5=classpath/

Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value1}") private String value1;
    @Value("${value2}") private String value2;
    @Value("${value3}") private String value3;
    @Value("${value4}") private String value4;
    @Value("${value5}") private String value5;

    public void hello() {
        System.out.println("value1=" + value1);
        System.out.println("value2=" + value2);
        System.out.println("value3=" + value3);
        System.out.println("value4=" + value4);
        System.out.println("value5=" + value5);
    }
}

실행

$ java -jar build/libs/spring-boot-sample.jar --spring.config.location=other.properties

value1=classpath/
value2=classpath/config
value3=currentdir/
value4=currentdir/config
value5=other

우선 순위에 맞게 설정을 덮어 쓰고 있다.

프로파일 지정

폴더 구성

|-application.properties
|-application-develop.properties
`-build/libs/
   `-spring-boot-sample.jar

application.properties

value=release module

application-develop.properties

value=develop module

실행

$ java -jar build/libs/spring-boot-sample.jar

value=release module

$ java -jar build/libs/spring-boot-sample.jar --spring.profiles.active=develop

value=develop module
  • 프로퍼티 파일을 application-{프로파일 이름}.properties 형식으로 작성한다.
  • 커멘트 라인 인수로 spring.profiles.active에 사용하려는 프로파일 이름을 지정한다.    - 커멘드 라인 인수 이외에도 시스템 속성이나 OS의 환경 변수도 지정 가능.
  • 그러면 지정된 프로파일에 해당하는 속성 파일이 읽혀진다.

같은 접두사를 가지는 속성을 Bean에 매핑

코드 작성

application.properties

person.firstName=Sato
person.last-name=Taro
person.age=18

Person.java

package sample.springboot;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="person")
public class Person {

    private String firstName;
    private String lastName;
    private int age;

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

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void hello() {
        System.out.println(firstName + " " + lastName + " : " + age);
    }
}

Main.java

package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Person person = ctx.getBean(Person.class);
            person.hello();
        }
    }
}

실행 결과

Sato Taro : 18
  • @ConfigurationProperties을 사용하면 특정 접두사와 속성들을 Bean에 매핑 할 수 있다.    - Bean은 setter 메소드가 필요하다.    - 필드의 이름은 낙타 대문자(camelcase) 이외에도 하이픈(-) 구분과 언더스코어(_) 경우에도 매핑을해 준다.
  • 이 구조를 활성화하려면 @EnableConfigurationProperties 어노테이션을 추가를 해야 한다.    - 엄밀히 말하면, @Configuration 어노테이션이 부여 된 클래스에 추가한다.

Yaml 사용

설정 파일을 application.yaml으로 하면, Yaml이 사용 가능하다.

기본 매핑

application.yaml

aaa:
    bbb:
        ccc: Hoge
        ddd: Fuga
    eee:
        fff: Piyo

Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${aaa.bbb.ccc}") private String ccc;
    @Value("${aaa.bbb.ddd}") private String ddd;
    @Value("${aaa.eee.fff}") private String fff;

    public void hello() {
        System.out.println("ccc=" + ccc);
        System.out.println("ddd=" + ddd);
        System.out.println("fff=" + fff);
    }
}

실행 결과

ccc=Hoge
ddd=Fuga
fff=Piyo

목록 매핑

application.yaml

myconf:
    list:
        - hoge
        - fuga
        - piyo

MyConf.java

package sample.springboot;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="myconf")
public class MyConfig {

    private List<String> list;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

Main.java

package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyConfig conf = ctx.getBean(MyConfig.class);
            System.out.println(conf.getList());
        }
    }
}

실행 결과

[hoge, fuga, piyo]
  • Bean에 매핑을 이용하면 List에 매핑도 가능하게 된다.

속성 파일 이외의 설정 값을 전달

Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value}") private String value;

    public void hello() {
        System.out.println("value=" + value);
    }
}

커멘트 라인 인수

$ java -jar build/libs/spring-boot-sample.jar --value=commandline

value=commandline
  • --[속성 이름]=[값]에서 커멘트 라인 인수에서 설정 값을 전달한다.

Java 시스템 속성

$ java -Dvalue=systemproperty -jar build/libs/spring-boot-sample.jar

value=systemproperty
  • --D [속성 이름]=[값]에서 시스템 속성에서 설정 값을 전달한다.

OS 환경 변수

$ set value=osenvironment

$ java -jar build/libs/spring-boot-sample.jar

value=osenvironment

※ OS는 Windows이다.

기본 속성

Main.java

package sample.springboot;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("value", "default property");

        SpringApplication app = new SpringApplication(Main.class);
        app.setDefaultProperties(properties);

        try (ConfigurableApplicationContext ctx = app.run(args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value}") private String value;

    public void hello() {
        System.out.println("value=" + value);
    }
}

실행결과

value=default property

SpringApplication#setDefaultProperties(Map<String, Object>)으로 디폴드 설정을 지정가능하다.

속성 우선 순위

속성 파일의 경우와 마찬가지로, 설정 값의 전달 방법에는 우선 순위가 있으며 우선 순위가 높은 방법이 낮은 방법으로 지정된 설정 값을 덮어 쓰기한다.

우선 순위는 다음과 같이 되어있다.

  1. 커멘드 라인 인수
  2. JNDI의 java : comp / env로부터 취득한 속성
  3. 시스템 속성
  4. OS 환경 변수
  5. jar 외부의 프로필 지정된 속성 파일
  6. jar 안에 있는 프로필 지정된 속성 파일
  7. jar 밖에 있는 속성 파일
  8. jar 안에 있는 속성 파일
  9. @PropertySource에서 지정된 속성 파일
  10. 기본 속성

숫자가 작은 쪽이 우선 순위가 높다.




최종 수정 : 2017-12-17