How to Dynamically Refresh Spring Boot Configurations with @RefreshScope

This article explains why dynamic configuration refresh is needed in Spring Boot, describes the core principles of @RefreshScope, provides step‑by‑step implementation code, and shares best practices and troubleshooting tips for zero‑downtime configuration updates in production environments.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
How to Dynamically Refresh Spring Boot Configurations with @RefreshScope

In traditional Java applications, modifying configuration files requires restarting the service, which leads to service interruption, loss of in‑memory state, and complex deployment processes.

Service interruption: The service is unavailable during restart.

State loss: Temporary data in memory is cleared.

Operational complexity: Requires elaborate release procedures.

Spring Boot's @RefreshScope solves these problems by enabling hot configuration updates, allowing applications to reassemble like LEGO blocks without downtime.

1. Why Need Dynamic Refresh?

2. Core Principles of @RefreshScope

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 configuration 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 configuration changes.

Bean lifecycle management:

Destroys and re‑initializes Beans marked with @RefreshScope.

3. Complete Implementation Steps

Step 1: Add Required Dependencies

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot starter -->
    <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 starter -->
    <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 configuration refresh
public class DynamicConfigApp {
    public static void main(String[] args) {
        SpringApplication.run(DynamicConfigApp.class, args);
    }
}

Step 3: Configure application.yml

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

# Expose refresh endpoint (critical!)
management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info

Step 4: Create a Dynamically Refreshable Bean

@Service
@RefreshScope // Mark this Bean as refreshable
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: Create 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 properties):

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

4. Deep Dive into @RefreshScope

1. Scope Proxy Mechanism (Pseudo‑code)

// Simplified proxy implementation
public class RefreshScopeProxy implements ApplicationContextAware {
    private Object targetBean;

    @Override
    public Object invoke(Method method) {
        if (configChanged) {
            // Destroy old Bean
            context.destroyBean(targetBean);
            // Recreate Bean
            targetBean = context.getBean(beanName);
        }
        return method.invoke(targetBean, args);
    }
}

2. Refresh Scope Control Techniques

Scenario 1: Refresh only specific Bean properties

@Component
@RefreshScope
public class PaymentService {
    @Value("${payment.timeout}")
    private int timeout; // Refreshable

    private final String apiVersion = "v1.0"; // Not refreshable
}

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

5. 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: <redacted>

2. Automatic Refresh Solutions

Solution 1: Git webhook triggers refresh.

Solution 2: Config center integration (example with Nacos)

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

6. Common Troubleshooting

Problem 1: Refresh does not take effect

Ensure @RefreshScope is added to the Bean.

Verify the refresh endpoint returns the modified properties.

Check logs with logging.level.org.springframework.cloud=DEBUG.

Problem 2: Multiple instances are out of sync

# Use Spring Cloud Bus for synchronized refresh
curl -X POST http://host:port/actuator/bus-refresh

Problem 3: Configuration update causes memory leak

@PreDestroy
public void cleanUp() {
    // Release resources
}

7. Extended Use Cases

Dynamic feature toggles: enable/disable modules at runtime.

Runtime log level adjustment:

@RefreshScope
public class LogConfig {
    @Value("${logging.level.root}")
    private String logLevel; // Applied dynamically
}

Database connection pool tuning:

# Dynamically adjust pool size
spring.datasource.hikari.maximum-pool-size=20

Conclusion: Embrace Dynamic Configuration

By leveraging @RefreshScope, you achieve zero‑downtime configuration updates, instant parameter changes, more flexible operations, and optimal resource utilization. Follow best practices such as protecting sensitive configurations, integrating with a config center, and securing the refresh endpoint for production stability.

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.

Spring BootSpring Cloudactuatorrefreshscope
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.