1. 개요

이 사용방법(예제)에서는 Spring 애플리케이션에서 속성을 다시 로드하는 방법을 배웁니다 .

2. Spring에서 속성 읽기

Spring에서 속성에 액세스할 수 있는 여러 가지 옵션이 있습니다.

  1. 환경 — 환경을 주입한 다음 주어진 속성을 읽기 위해 Environment#getProperty를 사용할 수 있습니다. 환경에는 시스템 속성, -D 매개변수 및 application.properties(.yml) 와 같은 다양한 속성 소스가 포함됩니다 . @PropertySource를 사용하여 환경 에 추가 속성 소스를 추가할 수도 있습니다 .
  2. Properties — 속성 파일을 Properties 인스턴스 에 로드한 다음 properties.get("property")을 호출하여 bean에서 사용할 수 있습니다.
  3. @Value — @Value(${'property'}) 어노테이션을  사용하여 빈에 특정 속성을 주입할 수 있습니다.
  4. @ConfigurationProperties @ConfigurationProperties를 사용하여  빈에서 계층적 속성을 로드할 수 있습니다.

3. 외부 파일에서 속성 다시 로드

런타임 중에 파일의 속성을 변경하려면 해당 파일을 jar 외부에 배치해야 합니다. 그런 다음 명령줄 매개  변수 –spring.config.location=file://{파일 경로}를 사용하여 Spring에 위치를 알려줍니다 . 또는 application.properties에 넣을 수 있습니다 .

파일 기반 속성에서 파일을 다시 로드하는 방법을 선택해야 합니다. 예를 들어 엔드포인트 또는 스케줄러를 개발하여 파일을 읽고 속성을 업데이트할 수 있습니다.

파일을 다시 로드하는 편리한 라이브러리 중 하나는 Apache의 commons-configuration 입니다 . 다른 ReloadingStrategy 와 함께 PropertiesConfiguration을 사용할 수 있습니다 .

pom.xmlcommons-configuration을 추가해봅시다 :

<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.10</version>
</dependency>

그런 다음 나중에 사용할 PropertiesConfiguration 빈을 생성하는 메서드를 추가합니다 .

@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public PropertiesConfiguration propertiesConfiguration(
  @Value("${spring.config.location}") String path) throws Exception {
    String filePath = new File(path.substring("file:".length())).getCanonicalPath();
    PropertiesConfiguration configuration = new PropertiesConfiguration(
      new File(filePath));
    configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
    return configuration;
}

위의 코드에서 FileChangedReloadingStrategy를 기본 새로 고침 지연이 있는 다시 로드 전략으로 설정했습니다 . 이는 PropertiesConfiguration이 마지막 확인이 5000ms 이전인 경우 파일 수정 날짜를 확인함을 의미합니다 .

FileChangedReloadingStrategy#setRefreshDelay를 사용하여 지연을 사용자 정의할 수 있습니다 .

3.1. 환경 속성 다시 로드

환경 인스턴스를 통해 로드된 속성을 다시 로드하려면 PropertySource를 확장한 다음 PropertiesConfiguration을 사용하여 외부 속성 파일에서 새 값을 반환해야 합니다 .

PropertySource 확장부터 시작하겠습니다 .

public class ReloadablePropertySource extends PropertySource {

    PropertiesConfiguration propertiesConfiguration;

    public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) {
        super(name);
        this.propertiesConfiguration = propertiesConfiguration;
    }

    public ReloadablePropertySource(String name, String path) {
        super(StringUtils.hasText(name) ? path : name);
        try {
            this.propertiesConfiguration = new PropertiesConfiguration(path);
            this.propertiesConfiguration.setReloadingStrategy(new FileChangedReloadingStrategy());
        } catch (Exception e) {
            throw new PropertiesException(e);
        }
    }

    @Override
    public Object getProperty(String s) {
        return propertiesConfiguration.getProperty(s);
    }
}

PropertiesConfiguration#getProperty 에 Delegation하기 위해 getProperty 메서드를 재정의했습니다 . 따라서 새로 고침 지연에 따라 간격으로 업데이트된 값을 확인합니다.

이제 ReloadablePropertySource를 Environment 의 속성 소스 추가합니다 .

@Configuration
public class ReloadablePropertySourceConfig {

    private ConfigurableEnvironment env;

    public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) {
        this.env = env;
    }

    @Bean
    @ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
    public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) {
        ReloadablePropertySource ret = new ReloadablePropertySource("dynamic", properties);
        MutablePropertySources sources = env.getPropertySources();
        sources.addFirst(ret);
        return ret;
    }
}

동일한 키로 기존 속성을 재정의하기를 원하기 때문에 새 속성 소스를 첫 번째 항목으로 추가했습니다 .

Environment 에서 속성을 읽는 빈을 만들어 봅시다 .

@Component
public class EnvironmentConfigBean {

    private Environment environment;

    public EnvironmentConfigBean(@Autowired Environment environment) {
        this.environment = environment;
    }

    public String getColor() {
        return environment.getProperty("application.theme.color");
    }
}

다른 재로드 가능한 외부 속성 소스를 추가해야 하는 경우 먼저 사용자 정의 PropertySourceFactory를 구현해야 합니다 .

public class ReloadablePropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String s, EncodedResource encodedResource)
      throws IOException {
        Resource internal = encodedResource.getResource();
        if (internal instanceof FileSystemResource)
            return new ReloadablePropertySource(s, ((FileSystemResource) internal)
              .getPath());
        if (internal instanceof FileUrlResource)
            return new ReloadablePropertySource(s, ((FileUrlResource) internal)
              .getURL()
              .getPath());
        return super.createPropertySource(s, encodedResource);
    }
}

그런 다음 @PropertySource를 사용하여 구성 요소의 클래스에 어노테이션을 달 수 있습니다 .

@PropertySource(value = "file:path-to-config", factory = ReloadablePropertySourceFactory.class)

3.2. 속성 인스턴스 다시 로드

환경은 Properties 보다 더 나은 선택입니다 . 특히 파일에서 속성을 다시 로드해야 하는 경우에 그렇습니다. 그러나 필요한 경우 java.util.Properties를 확장할 수 있습니다 .

public class ReloadableProperties extends Properties {
    private PropertiesConfiguration propertiesConfiguration;

    public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException {
        super.load(new FileReader(propertiesConfiguration.getFile()));
        this.propertiesConfiguration = propertiesConfiguration;
    }
  
    @Override
    public String getProperty(String key) {
        String val = propertiesConfiguration.getString(key);
        super.setProperty(key, val);
        return val;
    }
    
    // other overrides
}

getProperty 및 해당 오버로드를 재정의한 다음 PropertiesConfiguration 인스턴스에 Delegation 했습니다 . 이제 우리는 이 클래스의 빈을 생성하고 컴포넌트에 주입할 수 있습니다.

3.3. @ConfigurationProperties 로 빈 다시 로드하기

@ConfigurationProperties 로 동일한 효과를 얻으려면 인스턴스를 재구성해야 합니다. 그러나 Spring은 프로토타입 또는 요청 범위 를 가진 구성 요소의 새 인스턴스만 생성합니다  .

결과적으로 환경을 다시 로드하는 우리의 기술은 그들에게도 작동하지만 싱글톤의 경우 bean을 파괴하고 재생성하기 위해 Endpoints을 구현하거나 bean 자체 내에서 속성 다시 로드를 처리하는 것 외에 선택의 여지가 없습니다.

3.4. @Value를 사용하여 Bean 다시 로드

@Value 어노테이션은 @ConfigurationProperties 와 동일한 제한 사항을 나타냅니다 .

4. 액추에이터와 클라우드에 의한 속성 재로딩

Spring Actuator는 상태, 메트릭 및 구성에 대해 서로 다른 Endpoints을 제공하지만 새로 고침 빈에 대해서는 아무것도 제공하지 않습니다. 따라서 /refresh 엔드포인트를 추가하려면 Spring Cloud가 필요합니다. 이 엔드포인트는 Environment 의 모든 속성 소스를 다시 로드한 다음 EnvironmentChangeEvent 를 게시합니다 .

Spring Cloud는 @RefreshScope 도 도입했으며 구성 클래스 또는 빈에 사용할 수 있습니다. 결과적으로 기본 범위는 싱글톤 대신 새로고침이 됩니다 .

새로 고침 범위를 사용하여 Spring은 EnvironmentChangeEvent 에서 이러한 구성 요소의 내부 캐시를 지웁니다 . 그런 다음 Bean에 대한 다음 액세스에서 새 인스턴스가 작성됩니다.

pom.xmlspring-boot-starter-actuator를 추가하여 시작하겠습니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

그런 다음 spring-cloud-dependencies를 가져옵니다 .

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<properties>
    <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>

다음으로 spring-cloud-starter를 추가합니다 .

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter</artifactId>
</dependency>

마지막으로 새로 고침 Endpoints을 활성화합니다.

management.endpoints.web.exposure.include=refresh

Spring Cloud를 사용할 때 구성 서버를 설정하여 속성을 관리할 수 있지만 외부 파일을 계속 사용할 수도 있습니다. 이제 속성을 읽는 두 가지 다른 방법인 @Value@ConfigurationProperties를 처리할 수 있습니다 .

4.1. @ConfigurationProperties 로 빈 새로 고침

@RefreshScope 와 함께 @ConfigurationProperties를 사용하는 방법을 보여드리겠습니다 .

@Component
@ConfigurationProperties(prefix = "application.theme")
@RefreshScope
public class ConfigurationPropertiesRefreshConfigBean {
    private String color;

    public void setColor(String color) {
        this.color = color;
    }

    //getter and other stuffs
}

우리 빈은 루트 "application" 에서 " color" 속성을 읽고 있습니다 . 테마” 속성 Spring 문서에 따라 setter 메서드가 필요합니다.

외부 구성 파일에서 " application.theme.color " 값을 변경한 후 /refresh를 호출하여  다음에 액세스할 때 bean에서 새 값을 가져올 수 있습니다.

4.2. @Value 로 빈 새로 고침

샘플 구성 요소를 만들어 보겠습니다.

@Component
@RefreshScope
public class ValueRefreshConfigBean {
    private String color;

    public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
        this.color = color;
    } 
    //put getter here 
}

새로 고침 과정은 위와 동일합니다.

그러나 /refresh는 명시적 싱글톤 범위 가 있는 Bean에 대해 작동하지 않는다는 점에 유의해야 합니다  .

5. 결론

이 기사에서는 Spring Cloud 기능을 사용하거나 사용하지 않고 속성을 다시 로드하는 방법을 배웠습니다. 또한 각 기술의 함정과 예외를 설명했습니다.

전체 코드는 GitHub 프로젝트에서 사용할 수 있습니다 .

Cloud footer banner