How to Refresh Spring Boot Config at Runtime Without Restart

This guide explains why traditional Java apps require restarts for config changes, introduces Spring Boot's @RefreshScope for hot configuration updates, details its core principles, provides step‑by‑step implementation, best practices, troubleshooting tips, and advanced use cases such as dynamic feature toggles and logging level adjustments.

Java Backend Technology
Java Backend Technology
Java Backend Technology
How to Refresh Spring Boot Config at Runtime Without Restart

Why dynamic configuration refresh is needed?

In traditional Java applications, modifying a configuration file requires restarting the service, which causes service interruption, loss of in‑memory state, and complex deployment procedures.

Service interruption: the application is unavailable during restart

State loss: temporary data stored in memory is cleared

Operational complexity: a complicated release workflow is needed

Spring Boot's @RefreshScope perfectly solves these issues by enabling hot configuration updates, allowing the application to be reassembled like Lego blocks without downtime.

@RefreshScope Core Principle

1. Working diagram

graph TD
    A[Modify configuration 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: when configuration changes, values annotated with @Value are rebound

Bean lifecycle management: Beans marked with @RefreshScope are destroyed and re‑initialized

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

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

Step 3: Configure application.yml

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

management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info

Step 4: Create a dynamic configuration Bean

@Service
@RefreshScope // mark 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 keys):

["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) {
            // destroy old bean
            context.destroyBean(targetBean);
            // create new bean
            targetBean = context.getBean(beanName);
        }
        return method.invoke(targetBean, args);
    }
}

2. Refresh scope control techniques

Only properties annotated with @Value are refreshed:

@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 made refreshable:

@Configuration
@RefreshScope
public class AppConfig {
    @Bean
    @RefreshScope
    public FeatureService featureService() {
        return new FeatureService();
    }

    @Value("${app.theme}")
    private String theme;
}

Production best practices

1. Security hardening

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 solutions

Git webhook auto‑refresh or configuration‑center integration (Nacos example):

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

Common troubleshooting

Issue 1: Refresh does not take effect

Ensure the Bean is annotated with @RefreshScope Verify the refresh endpoint returns the modified keys

Enable debug logging:

logging.level.org.springframework.cloud=DEBUG

Issue 2: Multiple instances are out of sync

Use Spring Cloud Bus to broadcast refresh events:

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

Issue 3: Configuration update causes memory leak

Clean up resources in a @PreDestroy method:

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

Extended scenarios

Dynamic feature toggle: feature.new-checkout.enabled=true Runtime log level adjustment via a Bean with @RefreshScope and @Value("${logging.level.root}") Database connection pool tuning:

spring.datasource.hikari.maximum-pool-size=20

Conclusion

By leveraging @RefreshScope, you achieve zero‑downtime configuration updates, instant effect of application parameters, more flexible operations, and optimal resource utilization. Follow the best‑practice recommendations—avoid dynamic refresh for sensitive data, combine with a configuration center, and protect the refresh endpoint in production—to fully harness dynamic configuration in the cloud‑native era.

Illustration of dynamic configuration refresh
Illustration of dynamic configuration refresh
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.