24.8 Type-safe Configuration Properties

@Value("${property}")주석을 사용하여 configuration properties 정보를 주입하는 것은 때로는 특히 여러 속성을 사용하여 작업하거나 데이터가 사실상 계층 적이기 때문에 번거로운 작업이 될 수 있습니다.

Spring Boot는 다음 예제와 같이 강력하게 유형이 지정된 bean이 응용 프로그램의 구성을 관리하고 유효하게하는 등록 정보로 작업하는 대체 방법을 제공합니다.

package com.example;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

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

@ConfigurationProperties("acme")
public class AcmeProperties {

	private boolean enabled;

	private InetAddress remoteAddress;

	private final Security security = new Security();

	public boolean isEnabled() { ... }

	public void setEnabled(boolean enabled) { ... }

	public InetAddress getRemoteAddress() { ... }

	public void setRemoteAddress(InetAddress remoteAddress) { ... }

	public Security getSecurity() { ... }

	public static class Security {

		private String username;

		private String password;

		private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

		public String getUsername() { ... }

		public void setUsername(String username) { ... }

		public String getPassword() { ... }

		public void setPassword(String password) { ... }

		public List<String> getRoles() { ... }

		public void setRoles(List<String> roles) { ... }

	}
}

위의 POJO는 다음 속성을 정의합니다.

  • acme.enabled값은 false기본적으로입니다.

  • acme.remote-address에서 강제로 String유형을 지정할 수 있습니다.

  • acme.security.username속성의 이름에 따라 이름이 결정되는 중첩 된 "security"객체가 있습니다. 특히 반환 유형은 전혀 사용되지 않으며 SecurityProperties 가 될 수 있습니다.

  • acme.security.password.

  • acme.security.roles,String의 컬렉션과 함께.

Getter와 setter는 Spring MVC와 마찬가지로 표준 Java Beans 프로퍼티 디스크립터를 통해 바인딩되기 때문에 보통 필수이다. 다음과 같은 경우에는 세터를 생략 할 수 있습니다.

  • Map들은 바인더로 변경 될 수 있기 때문에 초기화 될 때까지 getter가 필요하지만 setter 는 필요없습니다.

  • 컬렉션과 배열은 인덱스 (일반적으로 YAML) 또는 쉼표로 구분 된 단일 값 (속성)을 사용하여 액세스 할 수 있습니다. 후자의 경우 setter는 필수입니다. 항상 이러한 유형의 setter를 추가하는 것이 좋습니다. 콜렉션을 초기화하는 경우, 콜렉션이 불변이 아닌지 확인하십시오 (앞의 예와 같이).

  • 앞의 예제에서 Security와 같이 중첩 된 POJO 속성이 초기화 되면 setter가 필요하지 않습니다. 바인더가 기본 생성자를 사용하여 즉석에서 인스턴스를 만들려면 setter가 필요합니다.

어떤 사람들은 Project Lombok을 사용하여 getter와 setter를 자동으로 추가합니다. Lombok이 컨테이너에서 자동으로 개체를 인스턴스화 할 때 사용되는 특정 형식의 생성자를 생성하지 않았는지 확인합니다.

마지막으로 표준 Java Bean 특성 만 고려되며 정적 특성에 대한 바인딩은 지원되지 않습니다.

또한 @EnableConfigurationProperties다음 예와 같이 주석 에 등록 할 속성 클래스를 나열해야합니다 .

@EnableConfigurationProperties는 해당 클래스를 빈으로 등록하고 프로퍼티 값을 할당한다.

@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

그런식으로 @ConfigurationPropertiesbean이 등록 되면, bean은 일반적인 이름인 <prefix>-<fqn>을 가진다. <prefix>@ConfigurationProperties주석에 명시된 환경 키 접두사 이고 <fqn>은 bean의 완전한 이름이다. 주석이 접두어를 제공하지 않으면 bean의 완전한 이름 만 사용됩니다. 위의 예제에서 bean 이름은 acme-com.example.AcmeProperties이다.

위의 구성은 AcmeProperties에 대한 일반 bean을 작성합니다. @ConfigurationProperties 는 환경만을 다루고, 특히 컨텍스트에서 다른 빈을 주입하지 않는 것이 좋습니다 .

@EnableConfigurationProperties주석은 프로젝트에 자동으로 적용되어 @ConfigurationProperties로 주석 된 기존 Bean이 Environment에서 구성되도록 합니다. @EnableConfigurationProperties(AcmeProperties.class)MyConfiguration에 주석을 추가하는 대신, 다음 예제와 같이 AcmeProperties 을 Bean으로 만들 수 있습니다. :

@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {

	// ... see the preceding example

}

이 구성 스타일은 SpringApplication외부 YAML 구성에서 특히 잘 작동합니다. (다음 예제참조)

# application.yml

acme:
	remote-address: 192.168.1.1
	security:
		username: admin
		roles:
		  - USER
		  - ADMIN

# additional configuration as required

@ConfigurationProperties빈 을 사용하여 작업하려면 다음 예제와 같이 다른 빈과 동일한 방법으로 빈을 삽입 할 수 있습니다.

@Service
public class MyService {

	private final AcmeProperties properties;

	@Autowired
	public MyService(AcmeProperties properties) {
	    this.properties = properties;
	}

 	//...

	@PostConstruct
	public void openConnection() {
		Server server = new Server(this.properties.getRemoteAddress());
		// ...
	}

}

@ConfigurationProperties을 사용하면 IDE가 자신의 키에 대한 자동 완성을 제공하는 데 사용할 수있는 메타 데이터 파일을 생성 할 수 있습니다. 자세한 내용은 부록 B, 구성 메타 데이터 부록을 참조하십시오.

24.8.1 third-party components

@ConfigurationProperties 를 사용하여 클래스에 주석을 추가하는 것은 물론, public @Bean 메소드에서도 사용할 수 있습니다. 이렇게하면 컨트롤 외부에있는 third-party components에 properties을 바인딩하려는 경우 특히 유용 할 수 있습니다.

Environment특성 에서 Bean을 구성하려면 @ConfigurationProperties를 다음 예와 같이 Bean 등록에 추가하십시오 .

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
	...
}

another접두어로 정의 된 모든 특성은 앞의 AcmeProperties예제 와 유사한 방식으로 해당 AnotherComponent Bean에 맵핑 됩니다.

24.8.2 느슨한 바인딩

Spring Boot는 bean에 Environment속성을 바인딩 할 때 다소 완화 된 규칙을 사용 @ConfigurationProperties하므로 Environment속성 이름과 bean 속성 이름이 완전히 일치 할 필요는 없습니다 . 이 기능이 유용한 일반적인 예로는 대시로 구분 된 환경 속성 (예 : context-path바인딩 contextPath)과 자본화 된 환경 속성 (예 : PORT바인딩 port)이 있습니다.

예를 들어 다음 @ConfigurationProperties클래스를 생각해보십시오 .

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {

	private String firstName;

	public String getFirstName() {
		return this.firstName;
	}

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

}

앞의 예제에서 다음 속성 이름을 모두 사용할 수 있습니다.

표 24.1. 느슨한 바인딩

재산

노트

acme.my-project.person.first-name

케밥 (Kebab) 케이스 . 파일 .properties.yml파일 에 사용하는 것이 좋습니다 .

acme.myProject.person.firstName

표준 camel 케이스 구문.

acme.my_project.person.first_name

밑줄표기법. .properties.yml 파일에서 사용하기 위한 대체 형식 입니다.

ACME_MYPROJECT_PERSON_FIRSTNAME

시스템 환경 변수를 사용할 때 권장되는 대문자 형식.

prefix주석의 값 은 kebab case여야합니다* (소문자로 구분되고 -로 구분 됨. 예: acme.my-project.person).

표 24.2. 속성 소스 당 바인딩 규칙 완화

부동산 출처

단순한

명부

속성 파일

카멜 케이스, 케밥 케이스 또는 밑줄 표기법

[ ]또는 쉼표로 구분 된 값을 사용하는 표준 목록 구문

YAML 파일

카멜 케이스, 케밥 케이스 또는 밑줄 표기법

표준 YAML 목록 구문 또는 쉼표로 구분 된 값

환경 변수

분리 문자로 밑줄이있는 대문자 형식입니다. _속성 이름 내에서 사용해서는 안됩니다.

밑줄로 둘러싼 숫자 값 (예 : MY_ACME_1_OTHER = my.acme[1].other)

시스템 속성

카멜 케이스, 케밥 케이스 또는 밑줄 표기법

[ ]또는 쉼표로 구분 된 값을 사용하는 표준 목록 구문

가능한 경우 속성을 my.property-name=acme같은 소문자 kebab 형식으로 저장하는 것이 좋습니다.

Map속성에 바인딩 할 때 key에 소문자 영숫자가 또는 -이 아닌 다른 문자 가 포함 된 경우 대괄호 표기법을 사용하여 원래 값을 유지해야합니다. 키가 []로 둘러싸여 있지 않으면 영숫자가 아니거나 -가 아닌 문자는 모두 제거됩니다. 예를 들어 다음 속성을 Map에 바인딩하는 것을 고려하십시오 .

acme :
   map :
     "[/ key1]" : value1
     "[/ key2]" : value2
     / key3 : value3

위의 속성은 /key1, /key2그리고 key3Map의 키로 바인딩 됩니다.

24.8.3 복합 유형(Complex Types) 병합

목록이 둘 이상의 장소에서 구성된 경우 전체 목록을 대체하여 우선 적용됩니다.

예를 들어 기본적으로nullnamedescription속성이 있는 MyPojo오브젝트가 있다고 가정합니다 . 다음 예제에서는AcmePropertiesMyPojo 객체 목록을 표시 합니다 .

@ConfigurationProperties("acme")
public class AcmeProperties {

	private final List<MyPojo> list = new ArrayList<>();

	public List<MyPojo> getList() {
		return this.list;
	}

}

다음 구성을 고려하십시오.

acme:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

dev프로파일이 활성화되지 않은 경우, AcmeProperties.list는 이전에 정의된 대로 하나의 MyPojo 항목을 포함합니다. 그러나 dev 프로파일을 사용 가능한 경우에는 list 여전히 하나의 항목 (이름이 my another name 이고 null값인 설명) 만 포함됩니다. 이 구성은 두 번째 MyPojo인스턴스를 목록에 추가 하지 않으며 항목을 병합하지 않습니다.‌

List 이 여러 프로파일에 지정 된 경우 우선 순위가 가장 높은 프로파일 (하나만 사용)이 사용됩니다. 다음 예제를 고려하십시오.

acme:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

앞의 예에서, 만약 dev프로파일이 활성화되어있으면, AcmeProperties.list에 하나의 MyPojo 항목이 포함되어있습니다.(이름 my another name 이고, 설명 값은 null). YAML의 경우 쉼표로 구분 된 목록과 YAML 목록을 모두 사용하여 목록의 내용을 완전히 오버라이딩 할 수 있습니다.

Map 속성의 경우, 당신은 여러 소스에서 가져온 속성 값으로 바인딩 할 수 있습니다. 그러나 여러 소스에서 동일한 속성의 경우 우선 순위가 가장 높은 속성이 사용됩니다. 다음 예제에서는AcmePropertiesMap<String, MyPojo>을 노출합니다.

@ConfigurationProperties("acme")
public class AcmeProperties {

	private final Map<String, MyPojo> map = new HashMap<>();

	public Map<String, MyPojo> getMap() {
		return this.map;
	}

}

다음 구성을 고려하십시오.

acme:
  map:
    key1:
      name: my name 1
      description: my description 1
---
spring:
  profiles: dev
acme:
  map:
    key1:
      name: dev name 1
    key2:
      name: dev name 2
      description: dev description 2

dev프로파일이 활성화되지 않은 경우 AcmeProperties.mapkey1 가있는 항목이 하나 있습니다 (이름 my name 1및 설명 포함 my description 1). 경우] dev프로파일이 사용하지만, map키 포함 두 항목 key1 (의 이름 dev name 1및 설명 my description 1등) key2(의 이름 dev name 2과 설명 dev description 2).

위의 병합 규칙은 YAML 파일뿐만 아니라 모든 속성 소스의 속성에도 적용됩니다.

24.8.4 Properties Conversion

스프링 부트는 @ConfigurationProperties빈에 바인딩 할 때 외부 애플리케이션 특성을 올바른 유형으로 강제 변환하려고 시도합니다 . 사용자 정의 유형 변환이 필요한 경우 ConversionServicebean (conversionService 로 이름 지정된 bean ) 또는 사용자 정의 특성 편집기 ( CustomEditorConfigurerbean을 통한 ) 또는 custom Converters(bean 정의가 @ConfigurationPropertiesBinding로 주석 된)을 제공 할 수 있습니다.

이 빈은(사용자가 편집한?) 애플리케이션 라이프 사이클 중 매우 일찍 요청되므로 ConversionService가 사용중인 의존성을 제한해야 합니다. 일반적으로 필요한 어떤 종속성은 작성시 완전히 초기화되지 않을 수 있습니다. 구성 키 강제 변환이 필요하지 않은 경우 사용자 지정 ConversionService의 이름을 바꾸고 @ConfigurationPropertiesBinding으로 정규화된 사용자 지정 변환기에만 의존 할 수 있습니다.

기간 변환

스프링 부트 (Spring Boot)는 기간 표현을 위한 헌신적인 지원을 제공합니다. java.time.Duration속성을 노출하면 응용 프로그램 속성에서 다음 형식을 사용할 수 있습니다.

  • 일반 long표현 ( @DurationUnit가 지정 되지 않은 경우 기본 단위로 밀리 초 사용 )

  • java.time.Duration의해 사용 된 표준 ISO-8601 형식

  • 값과 단위가 결합 된 더 읽기 쉬운 형식 (예 : 10s10 초를 의미)

다음 예제를 고려하십시오.

@ConfigurationProperties("app.system")
public class AppSystemProperties {

	@DurationUnit(ChronoUnit.SECONDS)
	private Duration sessionTimeout = Duration.ofSeconds(30);

	private Duration readTimeout = Duration.ofMillis(1000);

	public Duration getSessionTimeout() {
		return this.sessionTimeout;
	}

	public void setSessionTimeout(Duration sessionTimeout) {
		this.sessionTimeout = sessionTimeout;
	}

	public Duration getReadTimeout() {
		return this.readTimeout;
	}

	public void setReadTimeout(Duration readTimeout) {
		this.readTimeout = readTimeout;
	}

}

30 초의 세션 타임 아웃을 지정하려면 30, PT30S, 30s모두 동일합니다. 500ms의 읽기 시간 제한은 500, PT0.5S그리고 500ms 형식 중 하나로 지정할 수 있습니다.

지원되는 유닛을 사용할 수도 있습니다. 이것들은:

  • ns 나노 초 동안

  • us 마이크로 초 동안

  • ms 밀리 초 동안

  • s 초 동안

  • m 분 동안

  • h 몇 시간 동안

  • d 몇일 동안

기본 단위는 밀리 초이며 @DurationUnit위 샘플에서 설명한대로 재정의 할 수 있습니다 .

Duration으로의 전환과 밀리초가 아닌 , 단순히 기간을 표현하기 위해 Long사용하는 이전 버전에서 업그레이드하는 경우 단위 (@DurationUnit사용) 를 정의해야 합니다 . 이렇게하면 훨씬 더 풍부한 형식을 지원하면서 투명한 업그레이드 경로가 제공됩니다.

데이터 크기 변환

Spring Framework는 DataSize크기를 바이트 단위로 표현할 수 있는 값 유형을 가지고 있습니다. DataSize속성 을 공개하면 응용 프로그램 속성에서 다음 형식을 사용할 수 있습니다.

  • 정규 long표현 (a @DataSizeUnit가 지정되어 있지 않은 한 바이트를 기본 단위로 사용 )

  • 값과 단위가 결합 된 더 읽기 쉬운 형식 (예 : 10MB10 메가 바이트를 의미)

다음 예제를 고려하십시오.

@ConfigurationProperties("app.io")
public class AppIoProperties {

	@DataSizeUnit(DataUnit.MEGABYTES)
	private DataSize bufferSize = DataSize.ofMegabytes(2);

	private DataSize sizeThreshold = DataSize.ofBytes(512);

	public DataSize getBufferSize() {
		return this.bufferSize;
	}

	public void setBufferSize(DataSize bufferSize) {
		this.bufferSize = bufferSize;
	}

	public DataSize getSizeThreshold() {
		return this.sizeThreshold;
	}

	public void setSizeThreshold(DataSize sizeThreshold) {
		this.sizeThreshold = sizeThreshold;
	}

}

10메가바이트의 버퍼 크기를 지정하려면 1010MB동일합니다. 256 바이트의 크기 임계 값은 256또는 256B로 지정할 수 있습니다 .

지원되는 유닛을 사용할 수도 있습니다. 이것들은:

  • B 바이트

  • KB 킬로바이트

  • MB 메가 바이트

  • GB 기가 바이트

  • TB 테라 바이트

기본 단위는 바이트이며 위 샘플에서 설명한대로 @DataSizeUnit 로 재정의 할 수 있습니다 .

단순히 Long크기를 표현하는 데 사용하는 이전 버전에서 업그레이드하는 @DataSizeUnit경우 스위치 옆에 바이트가 없으면 단위 (사용 ) 를 정의해야 합니다 DataSize. 이렇게하면 훨씬 더 풍부한 형식을 지원하면서 투명한 업그레이드 경로가 제공됩니다.

24.8.5 @ConfigurationProperties 유효성 검사

Spring Boot는 @ConfigurationProperties 클래스가 Spring의 @Validated주석으로 주석이 추가 될 때마다 클래스의 유효성 검사를 시도합니다 .구성 클래스에서 JSR-303 javax.validation 제약 주석을 직접 사용할 수 있습니다 . 이렇게하려면 다음 예와 같이 호환되는 JSR-303 구현이 클래스 경로에 있는지 확인한 다음 필드에 제약 조건 주석을 추가하십시오.

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

	@NotNull
	private InetAddress remoteAddress;
	// ... getters and setters

}

@Validated과 함께 configuration properties를 작성하는 메소드에 @Bean 주석을 달아 검증을 트리거 할 수도 있습니다.

중첩 된 속성도 바운딩 될 때 유효성이 검사되지만 연관된 필드에도 @Valid주석을 추가하는 것이 좋습니다 . 이렇게하면 중첩 된 속성이없는 경우에도 유효성 검사가 트리거됩니다. 다음 예제는 앞의 AcmeProperties예제 를 기반으로합니다 .

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

	@NotNull
	private InetAddress remoteAddress;

	@Valid
	private final Security security = new Security();

	// ... getters and setters

	public static class Security {

		@NotEmpty
		public String username;

		// ... getters and setters

	}

}

또한 configurationPropertiesValidator 라는 빈 정의를 생성하여 커스텀 Spring Validator을 추가 할 수있다 . @Bean메서드는 static으로 선언해야합니다. 구성 등록 정보 유효성 검사기는 응용 프로그램의 수명주기 초기에 만들어지며 @Bean메서드를 정적으로 선언 하면 @Configuration클래스 를 인스턴스화하지 않고도 Bean을 만들 수 있습니다 . 이렇게하면 초기 인스턴스 생성으로 인해 발생할 수있는 문제를 피할 수 있습니다. 설정하는 방법을 보여주는 속성 유효성 검사샘플 이 있습니다.

spring-boot-actuator모듈은 모든 노출 엔드 포인트 포함 @ConfigurationProperties콩. 웹 브라우저 /actuator/configprops에서 해당 JMX 엔드 포인트 를 가리 키 거나 사용하십시오. 자세한 내용은 " 프로덕션 준비 기능 "단원을 참조하십시오.

24.8.6 @ConfigurationProperties 대 @Value

@Value주석은 핵심 컨테이너 기능이며, type-safe ConfigurationProperties과 동일한 기능을 제공하지 않습니다. 다음 표는 @ConfigurationProperties@Value 지원되는 기능을 요약 한 것입니다 .

특색

@ConfigurationProperties

@Value

아니

아니

SpEL 평가

아니

자신의 구성 요소에 대한 구성 키 세트를 정의한 경우 @ConfigurationProperties주석이 달린 POJO로 그룹화하는 것이 좋습니다 . 또한 @Value은 느슨한 바인딩을 지원하지 않으므로 환경 변수를 사용하여 값을 제공해야하는 경우에는 적합하지 않습니다.

마지막으로 SpEL표현식을 작성할 수있는 동안 @Value이러한 표현식은 애플리케이션 특성 파일 에서 처리되지 않습니다 .

Last updated