기본적인 @ConfigurationProperties
, @PropertySource
사용
Spring Boot 기반 프로젝트에서 @ConfigurationProperties
로 지정된 bean은 처음 application이 startup 할 때 bean이 생성되고 여러 properties 파일에서 읽어와 Environment에 저장된 값을 가져와 해당 bean에 바인딩해 준다.
다음과 같이 @ConfigurationProperties
를 선언하고
@ConfigurationProperties(prefix = "someProperties")
@Data
public class SomeProperties {
private String someKey1;
private long someKey2;
//.. 이하 생략
}
값을 읽어올 properties 파일을 configuration에서 다음과 같이 호출해 준다.
@AutoConfiguration
@PropertySource("classpath:some.properties")
public class SomeConfiguration {
}
해당 properties 파일에 다음과 같이 키/값을 선언하였다면
someProperties.someKey1=key1
someProperties.someKey2=1234
해당 properties의 값이 SomeProperties
bean에 바인딩되어 사용하게 된다.
Spring Cloud Config Server 나 외부 설정 값 사용
@PropertySource
로 로컬 파일에서 값을 관리하지 않고 spring cloud config server를 사용하거나 외부 URL이나 File 주소를 지정하여 properties를 호출하여 사용할 수도 있다.
# spring cloud config server 사용 예
spring.cloud.config.url=http://config-server/
spring.config.import=optional:configserver:
# http 주소 호출 예
spring.config.import=optional:http://anotherConfigLocation.dev/test.properties
위 예는 각각의 경우를 나누어 소개하였는데 외부 설정 소스를 여러 곳을 지정하여 사용하는 것도 가능하다.
# spring cloud config server 사용 예
spring.cloud.config.url=http://config-server/
spring.config.import=optional:configserver:,optional:http://anotherConfigLocation.dev/test.properties
위의 예에서는 config server와 외부 http 주소 호출을 같이 지정한 경우인데
N개의 spring cloud config server를 호출하거나 N개의 외부 http 주소를 호출하거나, N 개의 파일을 호출하거나 또는 앞서 열거한 여러 소스의 호출을 같이 섞어 지정할 수도 있다.
만약 @PropertySource
를 사용하여 호출한 로컬의 properties 파일에도 동일한 property 키가 선언되어 있더라도 spring.cloud.import
로 호출한 property 키의 값이 우선 적용된다.
configserver나 외부 주소의 test.properties
에서 제공하는 properties에 다음과 같은 키/값이 있는데
someProperties.someKey1=key1
someProperties.someKey2=1234
어느 시점에 해당 값이 다음처럼 변경되어 제공되고 있다면
someProperties.someKey1=key2
someProperties.someKey2=5678
그 변경된 값을 다시 호출하여 현재 실행 중인 application을 재시작하지 않고 적용하고 싶게 된다.
@RefreshScope
사용하기
@ConfigurationProperties
bean의 값이 application이 시작 시 바인딩된 이후 변경된 값을 갱신할 수 있도록 Spring Cloud Context는 @RefreshScope
를 제공하고 있다.
@RefreshScope
을 기존 사용 중인 @ConfigurationProperties
class에 다음과 같이 추가해 준다.
(상위 interface에 추가해도 구현한 클래스에 일괄 적용된다.)
@RefreshScope
@ConfigurationProperties(prefix = "someProperties")
@Data
public class SomeProperties {
private String someKey1;
private long someKey2;
//.. 이하 생략
}
dependency에 Spring의 spring-boot-starter-actuator
를 추가하고
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
다음과 같이 refresh
endpoint를 노출해 준다.
management.endpoints.web.exposure.include=health,refresh,beans
RefreshEndpoint를 활성화하면 application을 운영하다 앞서 예처럼 config 서버의 properties가 변경된 경우 다음 예처럼 /actuator/refresh
주소로 POST 요청을 통해 변경된 값을 반영할 수 있다.
curl -X POST http://localhost:8080/actuator/refresh
fetch("/actuator/refresh", {
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Accept" : "application/json",
"Accept-Language" : "ko-KR"
}
})
.then(response => response.json());
서버가 실시간으로 변경된 properties를 반영하는 것을 확인할 수 있다.
refresh가 되면 bean 객체가 다시 재등록되기 때문에 properties bean 등록 후 추가해야 하는 작업이 있다면 InitializingBean
interface를 구현하여 afterPropertiesSet()
method를 정의해 두면 refresh 될 때마다 수행된다.
여러 application refresh 요청 처리
앞서 refresh는 하나의 application에 refresh 요청을 하여 갱신한 경우이다.
보통 여러 application을 띄우고 사용하기 때문에 refresh 요청을 여러 application에 전파하기 위해서는 몇 가지 방법들을 추가로 사용해야 한다.
먼저 eureka 같은 service discovery를 사용하면 현재 k8s에 떠있는 pod 목록 같은 것들을 확인할 수 있다.
이를 통해 요청을 해야 할 대상 application 목록을 확보하여 refresh 요청을 일괄로 할 수 있다.
또는 Spring Cloud Bus를 통해 refresh message를 queue에 쌓고 각 서버가 주기적으로 refresh 요청을 확인하여 갱신할 수도 있다.
자세한 사용은 관련 글을 찾아보면 된다.
Spring Cloud @RefreshScope
문서 참고
Spring Cloud 문서의 @RefreshScope 관련 내용을 옮겨보았다.
https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/application-context-services.html#refresh-scope
configuration이 변경되면 @RefreshScope
를 적용한 Spring @Bean은 특별하게 처리된다.
이 기능은 초기화될 때만 configuration이 inject 되는 stateful bean의 문제를 해결한다.
예를 들어, 앞서 사용한 예처럼 @ConfigurationProperties
를 통해 설정된 datasource url이 refresh
요청으로 인해 변경될 때 DataSource
에 열려있는 connection이 있는 경우, 해당 연결의 holder가 현재 수행 중인 작업을 완료할 수 있기를 원할 수 있다.
그런 다음 pool에서 새 connection을 획득할 때 새 URL을 가진 connection을 얻게 된다.
때로는 한 번만 초기화할 수 있는 일부 bean에 @RefreshScope
를 적용해야 할 수도 있다.
bean이 불변(immutable)인 경우 @RefreshScope
annotation을 달거나 spring.cloud.refresh.extra-refreshable
property에 classname을 지정해야 한다.
[!WARNING]
다만HikariDataSource
인DataSource
bean이 있는 경우 refresh 되지 않는다.
이 bean은spring.cloud.refresh.never-refreshable
의 기본 값이다.
refresh 해야 하는 경우 다른DataSource
구현을 사용해야 한다.
Refresh scope bean은 사용 시 (즉, method가 호출될 때) 초기화되는 lazy proxy이며, scope는 초기화된 값의 캐시 역할을 한다.
다음 method 호출 시 bean을 강제로 다시 초기화하려면 해당 cache 항목을 무효화(invalidate) 해야 한다.
RefresnEndpoint
는 모든 대상 bean을 refresh 하는 RefreshScope
의 refreshAll
method 호출만 제공하고 있다.
하지만 RefreshScope
엔 개별 bean만 refresh 할 수 있는 refresh(Class type)
, refresh(String name)
method를 제공하고 있다.
/refresh
endpoint를 노출하려면 다음과 같은 설정을 추가하면 된다.
management:
endpoints:
web:
exposure:
include: refresh
[!NOTE]
@RefreshScope
는 (기술적으로는)@Configuration
class에서만 동작하지만, 의외의 동작이 발생할 수 있다.
예를 들어 해당 class에 정의된 모든@Bean
이@RefreshScope
에 있다는 의미는 아니다.
특히, 해당 bean에 의존하는 모든 것은@RefreshScope
에 있지 않는 한 refresh가 시작될 때 업데이트 되는 것에 의존할 수 없다.
[!NOTE]
configuration value를 제거한 이후 refresh를 수행해도 configuration value의 존재가 업데이트되지 않는다.
refresh 후 value를 업데이트하려면 configuration property가 있어야 한다.
application에 값이 있는지 여부에 의존하는 경우, 해당 값의 부재에 의존하도록 로직을 전환하는 것이 좋다.
또 다른 방법은 application의 configuration에 존재하지 않고 값 변경에 의존하는 것이다.
[!WARNING]
context refresh는 Spring AOT 변환 및 native image에는 지원되지 않는다.
AOT 및 native image의 경우spring.cloud.refresh.enabled
를false
로 설정해야 한다.
Refresh Scope on Restart
restart 시 bean을 원활하게 refresh 하는 기능은 특히 JVM checkpoint restore와 함께 실행되는 (Project CRaC 같은) application에 유용하다.
이 기능을 허용하기 위해 이제 재시작 시 context refresh를 trigger 하여 configuration property를 rebinding 하고 @RefreshScope
로 annotation이 달린 모든 bean을 새로 고치는 RefreshScopeLifecycle
bean을 instance화 한다.spring.cloud.refresh.on-restart.enabled
를 false
로 설정하여 이 동작을 비활성화 할 수 있다.
'Study > Java' 카테고리의 다른 글
Spring Boot 3.4 Release Notes (0) | 2024.12.03 |
---|---|
hibernate SqlType.JSON (json data) 사용해 보기 (0) | 2024.11.18 |
springdoc swagger ui에 authorize 사용해 보기 (0) | 2024.11.07 |
JDK 23 New Features (0) | 2024.10.07 |
spring-boot-properties-migrator로 custom property report 하기 (0) | 2024.06.16 |
Antora 사용해 보기 (0) | 2024.06.12 |
Spring Boot 3.3 Release Notes (0) | 2024.05.30 |
SonarQube, SonarLint issue 예외 처리하기 (0) | 2024.05.11 |
JDK 22 New Features (0) | 2024.03.21 |
Eclipse (STS) 에서 Parameter Name Hint 사용하기 (0) | 2024.03.08 |