How to Refresh Spring Boot Configurations at Runtime with @RefreshScope

This article explains why traditional Java apps require restarts for config changes, introduces Spring Boot's @RefreshScope for zero‑downtime hot‑reloading, details its core principles, provides step‑by‑step implementation, troubleshooting tips, best practices, and advanced use cases for dynamic configuration in production environments.

Architect
Architect
Architect
How to Refresh Spring Boot Configurations at Runtime with @RefreshScope

Why Dynamic Configuration Refresh Is Needed

In traditional Java applications, changing a configuration file requires a service restart, which leads to service interruption, loss of in‑memory state, and complex deployment procedures.

Service interruption: the application is unavailable during restart.

State loss: temporary data held in memory is cleared.

Operational complexity: requires elaborate release workflows.

Spring Boot’s @RefreshScope solves these problems by enabling hot‑updates of configuration without restarting the service.

@RefreshScope Core Principle

1. Working Diagram

graph TD
    A[Modify config file] --> B[Send POST refresh request]
    B --> C[/actuator/refresh endpoint]
    C --> D[RefreshScope refresh mechanism]
    D --> E[Destroy old Bean and create new Bean]
    E --> F[New config takes effect immediately]

2. Key Technical Details

Scope proxy: creates a dynamic proxy for the Bean to intercept method calls.

Configuration binding: re‑binds values annotated with @Value when the configuration changes.

Bean lifecycle management: destroys and re‑initialises Beans marked with @RefreshScope.

Complete Implementation Steps

Step 1 – Add Required Dependencies

<!-- pom.xml -->
<dependencies>
  <!-- Spring Boot core -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!-- Actuator for refresh endpoint -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <!-- Spring Cloud Config support -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter</artifactId>
    <version>3.1.3</version>
  </dependency>
</dependencies>

Step 2 – Enable Refresh Mechanism

// Main application class
@SpringBootApplication
@EnableRefreshScope // enable dynamic refresh
public class DynamicConfigApp {
    public static void main(String[] args) {
        SpringApplication.run(DynamicConfigApp.class, args);
    }
}

Step 3 – Configure application.yml

# Basic application config
app:
  feature:
    enabled: true
    timeout: 5000
    retry-count: 3
    welcome-msg: "Hello, Dynamic Config!"

# Expose refresh endpoint
management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info

Step 4 – Create a Dynamically Refreshable Bean

@Service
@RefreshScope // Bean supports runtime refresh
public class FeatureService {
    @Value("${app.feature.enabled}")
    private boolean featureEnabled;
    @Value("${app.feature.timeout}")
    private int timeout;
    @Value("${app.feature.retry-count}")
    private int retryCount;
    @Value("${app.feature.welcome-msg}")
    private String welcomeMessage;

    public String getFeatureConfig() {
        return String.format("""
            Feature Enabled: %s
            Timeout: %d ms
            Retry Count: %d
            Message: %s
            """, featureEnabled, timeout, retryCount, welcomeMessage);
    }
}

Step 5 – Add a Test Controller

@RestController
@RequestMapping("/config")
public class ConfigController {
    private final FeatureService featureService;
    public ConfigController(FeatureService featureService) {
        this.featureService = featureService;
    }
    @GetMapping
    public String getConfig() {
        return featureService.getFeatureConfig();
    }
}

Step 6 – Trigger Configuration Refresh

After modifying application.yml, send a POST request to the refresh endpoint:

curl -X POST http://localhost:8080/actuator/refresh

Example response (list of changed keys):

["app.feature.timeout", "app.feature.welcome-msg"]

Deep Dive into @RefreshScope

1. Scope Proxy Mechanism (pseudo‑code)

public class RefreshScopeProxy implements ApplicationContextAware {
    private Object targetBean;
    @Override
    public Object invoke(Method method) {
        if (configChanged) {
            // destroy old bean
            context.destroyBean(targetBean);
            // create new bean
            targetBean = context.getBean(beanName);
        }
        return method.invoke(targetBean, args);
    }
}

2. Refresh Scope Control Techniques

Scenario 1 – Refresh specific Bean properties only

@Component
@RefreshScope
public class PaymentService {
    @Value("${payment.timeout}")
    private int timeout; // refreshed
    private final String apiVersion = "v1.0"; // not refreshed
}

Scenario 2 – Refresh an entire configuration class

@Configuration
@RefreshScope
public class AppConfig {
    @Bean
    @RefreshScope
    public FeatureService featureService() {
        return new FeatureService();
    }
    @Value("${app.theme}")
    private String theme;
}

Production Best Practices

1. Secure the Refresh Endpoint

management:
  endpoint:
    refresh:
      enabled: true
  endpoints:
    web:
      exposure:
        include: refresh
  base-path: /internal
  path-mapping:
    refresh: secure-refresh

spring:
  security:
    user:
      name: admin
      password: $2a$10$NVM0n8ElaRgg7zWO1CxUdei7vWoQP91oGycgVNCY8GQEx.TGx.AaC

2. Automatic Refresh via Config Center (Nacos example)

# bootstrap.yml
spring:
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        auto-refresh: true

Common Issues and Troubleshooting

Refresh not taking effect – ensure the Bean is annotated with @RefreshScope.

Refresh endpoint returns no changes – verify that the endpoint response lists modified keys.

Multiple instances out of sync – use Spring Cloud Bus ( curl -X POST http://host:port/actuator/bus-refresh) to broadcast refresh events.

Potential memory leaks – clean up resources in a @PreDestroy method.

Extended Scenarios

Dynamic feature toggles, runtime log‑level adjustments, and database connection‑pool tuning can all be driven by refreshed configuration:

# Enable a new feature instantly
feature.new-checkout.enabled=true

# Adjust log level at runtime
logging.level.root=DEBUG

# Change Hikari pool size dynamically
spring.datasource.hikari.maximum-pool-size=20

Conclusion

✅ Zero‑downtime configuration updates.

✅ Immediate effect of application parameters.

✅ More flexible operational experience.

✅ Maximised resource utilisation.

Best‑practice recommendations:

Avoid using dynamic refresh for sensitive data such as passwords.

Combine with a configuration centre (Nacos, Config Server) for centralized management.

Secure the refresh endpoint in production environments.

Mastering dynamic configuration refresh empowers your Spring Boot applications to stay agile and resilient in cloud‑native deployments.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend DevelopmentDynamic ConfigurationSpring Boot@RefreshScoperuntime refresh
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.