[java]SpringBoot 의 @ConfigurationProperties 간략 정리
intro
- Spring Boot는 외부 설정에 정의된 속성에 대해 쉽게 사용할수있도록 @ConfigurationProperties 어노테이션을 제공한다.
useage
설정
- maven 이나 gradle 설정은 알아서 잘 하자
- spring-boot-starter-parent 가 필요하고, 속성값에 대한 검증을 하려면 hibernate-validator 도 필요로 하다.
- http://hibernate.org/validator/documentation/getting-started/
기본설정 및 바인딩 룰
- http://hibernate.org/validator/documentation/getting-started/
- 우선 샘플로 하나 봐보자.
@Configuration @ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters }
- @Configuration 어노테이션을 주어 bean 을 생성하도록 하자
- @ConfigurationProperties 는 동일한 접두사가 있는 경우 계층적으로 사용할수있도록 해준다.
- 아래처럼 되어있으면
@ConfigurationProperties(prefix = "mail.a") ... @ConfigurationProperties(prefix = "mail.b")
- 아래처럼 설정 가능하다
mail: a: xxxxxx b: xxxxxx
- 아래처럼 되어있으면
- Spring framework는 표준 Java bean setter를 사용하므로 각 속성에 대한 setter를 선언해야 한다.
- POJO에서 @Configuration 을 사용하지 않으면 기본 Spring 애플리케이션 클래스에 @EnableConfigurationProperties(ConfigProperties.class) 를 추가 하여 속성을 POJO에 바인딩해야 합니다.
@SpringBootApplication @EnableConfigurationProperties(ConfigProperties.class) public class EnableConfigurationDemoApplication { public static void main(String[] args) { SpringApplication.run(EnableConfigurationDemoApplication.class, args); } }
- POJO에서 @Configuration 을 사용하지 않으면 기본 Spring 애플리케이션 클래스에 @EnableConfigurationProperties(ConfigProperties.class) 를 추가 하여 속성을 POJO에 바인딩해야 합니다.
- Spring은 ConfigProperties 클래스 의 필드 중 하나와 동일한 이름과 접두사 mail 이 있는 속성 파일에 정의된 모든 속성을 자동으로 바인딩합니다 . 바인딩 규칙은 상당히 루즈하다. 아래와같은 변형 모두 바인딩이 된다.
mail.hostName mail.hostname mail.host_name mail.host-name mail.HOST_NAME
spring boot 2.2이후
- spring boot 2.2부터는 @ConfigurationProperties 를 사용하면 굳이 @Component나 @EnableConfigurationProperties를 사용할 필요가 없어졌다. @ConfigurationProperties 만 사용하면 알아서 스캔한다.
- 이제 아래처럼 해도 된다.
@ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters }
중첩속성
- 아래같은 경우
public class Credentials {
private String authMethod;
private String username;
private String password;
// standard getters and setters
}
public class ConfigProperties {
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
private Credentials credentials;
// standard getters and setters
}
- 아래와 같은 설정이 된다.
#Simple properties
mail.hostname=mailer@mail.com
mail.port=9000
mail.from=mailer@mail.com
#List properties
mail.defaultRecipients[0]=admin@mail.com
mail.defaultRecipients[1]=owner@mail.com
#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
#Object properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA1
속성 검증
- @ConfigurationProperties 는 JSR-303 형식을 사용하여 속성의 유효성 검사를 제공
- 유효성 검사 중 하나라도 실패하면 기본 응용 프로그램이 IllegalStateException 과 함께 시작되지 않음
- Hibernate Validation 프레임워크는 표준 Java Bean getter 및 setter를 사용하므로 각 속성에 대해 getter 및 setter를 선언해야함
- 필수 속성 지정
@NotBlank private String hostName;
- 속성을 1~4자 길이로 만들어 보기
@Length(max = 4, min = 1) private String authMethod;
- 1025에서 65536까지로 제한하기
@Min(1025) @Max(65536) private int port;
- 이메일 주소 형식과 일치해야하는 속성 부여
@Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$") private String from;
.
불변 @ConfigurationProperties 바인딩
- Spring Boot 2.2부터 @ConfigurationProperties 이 달린 클래스가 변경 불가능하도록 @ConstructorBinding 어노테이션을 사용하여 구석 속성을 바인딩할 수 있음.
- @ConstructorBinding을 사용할 때 바인딩하려는 모든 매개변수를 생성자에 제공해야 함.
- ImmutableCredentials의 모든 필드 final 이고 setter 메서드가 없음
- 생성자 바인딩을 사용하려면 @EnableConfigurationProperties 또는 @ConfigurationPropertiesScan 을 사용하여 구성 클래스를 명시적으로 활성화해야함.
@ConfigurationProperties(prefix = "mail.credentials")
@ConstructorBinding
public class ImmutableCredentials {
private final String authMethod;
private final String username;
private final String password;
public ImmutableCredentials(String authMethod, String username, String password) {
this.authMethod = authMethod;
this.username = username;
this.password = password;
}
public String getAuthMethod() {
return authMethod;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
Java16 에선
- Java 16 은 JEP 395의 일부로 레코드 유형을 도입했습니다 . 레코드는 변경할 수 없는 데이터의 투명한 전달자 역할을 하는 클래스입니다. 따라서 구성 보유자 및 DTO에 대한 완벽한 후보입니다. 사실 Spring Boot에서 Java 레코드를 구성 속성으로 정의할 수 있습니다 . 예를 들어 이전 예제는 다음과 같이 다시 작성할 수 있습니다.
@ConstructorBinding
@ConfigurationProperties(prefix = "mail.credentials")
public record ImmutableCredentials(String authMethod, String username, String password) {
}
- 또한 Spring Boot 2.6 부터 단일 생성자 레코드의 경우 @ConstructorBinding 주석을 삭제할 수 있습니다 . 그러나 레코드에 여러 생성자가 있는 경우 @ConstructorBinding 을 사용하여 속성 바인딩에 사용할 생성자를 식별해야 합니다.
참고용 예시들
- 아래와 같은 설정은
external-server: targets: notice: domain: https://naver.com context: notice
- 이렇게 하면 쉽게 이용 가능
@Data @Component @ConfigurationProperties(value="external-server") @Validated public class TargetProperties { private static final String DOMAIN = "domain"; private static final String CONTEXT = "context"; @NotNull private Map<String, Map<String, String>> targets; public String getDomain(final String productId) { Map<String, String> productMap = targets.get(productId); return productMap != null ? productMap.get(DOMAIN) : null; } public String getContext(final String productId) { Map<String, String> productMap = targets.get(productId); return productMap != null ? productMap.get(CONTEXT) : null; } /** * yaml에 선언된 product id 획득 * notice / ... */ public List<String> getProductIds() { Map<String, Map<String, String>> products = this.targets; List<String> ids = new ArrayList<>(products.size()); products.forEach((k,v) -> ids.add(k)); return ids; } }
- 아래처럼도 이용 가능
game-info: games: AA: infoA BB: infoB adUrl: AA: URLA BB: URLB
@Data
@Component
@ConfigurationProperties(value="game-info")
@Validated
public class GameInfoProperties {
@NotNull
private Map<String, String> games;
@NotNull
private Map<String, String> adUrl;
public String getGameString(final String gameId) {
return games.get(gameId);
}
public String getAdUrl(final String gameId) {
return adUrl.get(gameId);
}
}
참고
- https://www.baeldung.com/configuration-properties-in-spring-boot