1. 개요

이 예제에서는 Spring 애플리케이션에서 속성 을 다시로드하는 방법을 보여줄 것 입니다.

2. Spring에서 속성 읽기

Spring에서 속성에 액세스하는 다른 옵션이 있습니다.

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

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

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

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

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

pom.xml에 commons-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. 환경 속성 다시로드

Environment 인스턴스를 통해로드 된 속성을 다시로드 하려면 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);
    }
}

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

이제 ReloadablePropertySourceEnvironment 의 속성 소스 에 추가 할 것입니다.

@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 인스턴스에 위임했습니다 . 이제이 클래스의 빈을 생성하여 컴포넌트에 삽입 할 수 있습니다.

3.3. @ConfigurationProperties로 Bean 다시로드

@ConfigurationProperties로 동일한 효과를 얻으려면 인스턴스를 재구성해야합니다.

그러나 Spring은 프로토 타입 또는 요청  범위가 있는 새 구성 요소 인스턴스 만 생성합니다 .

따라서 환경을 다시로드하는 우리의 기술도 작동하지만 싱글 톤의 경우 엔드 포인트를 구현하여 Bean을 파괴하고 다시 생성하거나 Bean 자체 내에서 속성 다시로드를 처리하는 것 외에는 선택의 여지가 없습니다.

3.4. @Value로 Bean 다시로드

@Value 어노테이션과 같은 제한 제시 @ConfigurationProperties을 .

4. 액추에이터 및 클라우드로 속성 다시로드

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

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

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

spring-boot-starter-actuatorpom.xml 에 추가하는 것으로 시작하겠습니다 .

<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>

마지막으로 새로 고침 끝점을 활성화하겠습니다.

management.endpoints.web.exposure.include=refresh

Spring Cloud를 사용할 때 속성을 관리 하기 위해 Config Server설정할 수 있지만 외부 파일도 계속 사용할 수 있습니다. 이제 속성을 읽는 두 가지 다른 메서드 인 @Value@ConfigurationProperties를 처리 할 수 ​​있습니다 .

4.1. @ConfigurationProperties로 Bean 새로 고침

@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
}

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

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

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 는 명시적인 싱글 톤  범위를 가진 빈에서는 작동하지 않는다는 점에 유의해야합니다 .

5. 결론

이 예제에서는 Spring Cloud 기능을 사용하거나 사용하지 않고 속성을 다시로드하는 방법을 보여주었습니다. 또한 각 기술의 함정과 예외를 보여주었습니다.

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