Refresh Spring Boot Config at Runtime Without Restart

This guide explains how to use Spring Boot's @RefreshScope to dynamically refresh configuration at runtime without restarting services, covering the underlying mechanism, step‑by‑step implementation, production best practices, troubleshooting tips, and advanced use cases.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Refresh Spring Boot Config at Runtime Without Restart

Why dynamic configuration refresh is needed?

In traditional Java apps, changing a config file requires restarting the service, causing downtime, 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.

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

Scope proxy: creates a dynamic proxy for beans 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.

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 for config refresh -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter</artifactId>
        <version>3.1.3</version>
    </dependency>
</dependencies>

Step 2: Enable refresh mechanism

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

Step 3: Configure application.yml

# Application basic 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 dynamic configuration bean

@Service
@RefreshScope
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 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 /actuator/refresh:

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

The response lists the changed properties, e.g., ["app.feature.timeout","app.feature.welcome-msg"].

Deep dive into @RefreshScope

1. Scope proxy principle (pseudo‑code)

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

2. Refresh scope control tricks

Only properties annotated with @Value are refreshed; other fields remain unchanged.

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

    private final String apiVersion = "v1.0"; // not refreshed
}

Whole configuration classes can also be refreshed:

@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: <encrypted>

2. Automatic refresh solutions

Git webhook or configuration center (e.g., Nacos) can trigger automatic refresh.

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

Common troubleshooting

Problem 1: Refresh does not take effect

Ensure the bean is annotated with @RefreshScope.

Verify the refresh endpoint returns the modified properties.

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

Problem 2: Multi‑instance refresh out of sync

Use Spring Cloud Bus to broadcast refresh events:

curl -X POST http://host:port/actuator/bus-refresh

Problem 3: Configuration update causes memory leak

Clean up resources in a @PreDestroy method.

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

Extended scenarios

Dynamic feature toggles, runtime log level adjustment, and database connection pool tuning can all be handled with @RefreshScope:

# Enable new checkout feature at runtime
feature.new-checkout.enabled=true

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

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

Conclusion

By leveraging @RefreshScope, you achieve zero‑downtime configuration updates, immediate effect of application parameters, more flexible operations, and optimal resource utilization. Follow best‑practice recommendations such as protecting the refresh endpoint and avoiding dynamic refresh for sensitive credentials.

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.

JavaDynamic ConfigurationSpring Boot@RefreshScope
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.