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는 해당 클래스를 빈으로 등록하고 프로퍼티 값을 할당한다.
그런식으로 @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으로 만들 수 있습니다. :
이 구성 스타일은 SpringApplication외부 YAML 구성에서 특히 잘 작동합니다. (다음 예제참조)
@ConfigurationProperties빈 을 사용하여 작업하려면 다음 예제와 같이 다른 빈과 동일한 방법으로 빈을 삽입 할 수 있습니다.
@ConfigurationProperties을 사용하면 IDE가 자신의 키에 대한 자동 완성을 제공하는 데 사용할 수있는 메타 데이터 파일을 생성 할 수 있습니다. 자세한 내용은 부록 B, 구성 메타 데이터 부록을 참조하십시오.
24.8.1 third-party components
@ConfigurationProperties 를 사용하여 클래스에 주석을 추가하는 것은 물론, public @Bean 메소드에서도 사용할 수 있습니다. 이렇게하면 컨트롤 외부에있는 third-party components에 properties을 바인딩하려는 경우 특히 유용 할 수 있습니다.
Environment특성 에서 Bean을 구성하려면 @ConfigurationProperties를 다음 예와 같이 Bean 등록에 추가하십시오 .
another접두어로 정의 된 모든 특성은 앞의 AcmeProperties예제 와 유사한 방식으로 해당 AnotherComponent Bean에 맵핑 됩니다.
24.8.2 느슨한 바인딩
Spring Boot는 bean에 Environment속성을 바인딩 할 때 다소 완화 된 규칙을 사용 @ConfigurationProperties하므로 Environment속성 이름과 bean 속성 이름이 완전히 일치 할 필요는 없습니다 . 이 기능이 유용한 일반적인 예로는 대시로 구분 된 환경 속성 (예 : context-path바인딩 contextPath)과 자본화 된 환경 속성 (예 : PORT바인딩 port)이 있습니다.
예를 들어 다음 @ConfigurationProperties클래스를 생각해보십시오 .
앞의 예제에서 다음 속성 이름을 모두 사용할 수 있습니다.
표 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에 바인딩하는 것을 고려하십시오 .
위의 속성은 /key1, /key2그리고 key3가 Map의 키로 바인딩 됩니다.
24.8.3 복합 유형(Complex Types) 병합
목록이 둘 이상의 장소에서 구성된 경우 전체 목록을 대체하여 우선 적용됩니다.
예를 들어 기본적으로null인 name 및 description속성이 있는 MyPojo오브젝트가 있다고 가정합니다 . 다음 예제에서는AcmeProperties 의 MyPojo 객체 목록을 표시 합니다 .
다음 구성을 고려하십시오.
dev프로파일이 활성화되지 않은 경우, AcmeProperties.list는 이전에 정의된 대로 하나의 MyPojo 항목을 포함합니다. 그러나 dev 프로파일을 사용 가능한 경우에는 list여전히 하나의 항목 (이름이 my another name 이고 null값인 설명) 만 포함됩니다. 이 구성은 두 번째 MyPojo인스턴스를 목록에 추가 하지 않으며 항목을 병합하지 않습니다.
List 이 여러 프로파일에 지정 된 경우 우선 순위가 가장 높은 프로파일 (하나만 사용)이 사용됩니다. 다음 예제를 고려하십시오.
앞의 예에서, 만약 dev프로파일이 활성화되어있으면, AcmeProperties.list에 하나의 MyPojo 항목이 포함되어있습니다.(이름 my another name 이고, 설명 값은 null). YAML의 경우 쉼표로 구분 된 목록과 YAML 목록을 모두 사용하여 목록의 내용을 완전히 오버라이딩 할 수 있습니다.
Map 속성의 경우, 당신은 여러 소스에서 가져온 속성 값으로 바인딩 할 수 있습니다. 그러나 여러 소스에서 동일한 속성의 경우 우선 순위가 가장 높은 속성이 사용됩니다. 다음 예제에서는AcmeProperties 의 Map<String, MyPojo>을 노출합니다.
다음 구성을 고려하십시오.
dev프로파일이 활성화되지 않은 경우 AcmeProperties.map키 key1 가있는 항목이 하나 있습니다 (이름 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가 지정 되지 않은 경우 기본 단위로 밀리 초 사용 )
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 메가 바이트를 의미)
다음 예제를 고려하십시오.
10메가바이트의 버퍼 크기를 지정하려면 10와 10MB동일합니다. 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 구현이 클래스 경로에 있는지 확인한 다음 필드에 제약 조건 주석을 추가하십시오.
@Validated과 함께 configuration properties를 작성하는 메소드에 @Bean 주석을 달아 검증을 트리거 할 수도 있습니다.
중첩 된 속성도 바운딩 될 때 유효성이 검사되지만 연관된 필드에도 @Valid주석을 추가하는 것이 좋습니다 . 이렇게하면 중첩 된 속성이없는 경우에도 유효성 검사가 트리거됩니다. 다음 예제는 앞의 AcmeProperties예제 를 기반으로합니다 .
또한 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 지원되는 기능을 요약 한 것입니다 .
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
// ... see the preceding example
}
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
@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(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
@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;
}
}
@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
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
@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
@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;
}
}
@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;
}
}
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
@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
}
}