Introduction
Spring Cloud Config enables centralized configuration management with runtime refresh via /actuator/refresh. However, beans do not pick up new configuration values when they are not annotated with @RefreshScope, when the refresh endpoint is not exposed, or when configuration properties are bound at startup and never rebound. The @RefreshScope proxy mechanism creates a new bean instance on next access after a refresh event, but beans without this annotation retain their original values. Additionally, @Value injection and @ConfigurationProperties behave differently during refresh.
Symptoms
# Config server updated, refresh called, but values unchanged
curl -X POST http://localhost:8080/actuator/refresh
# Returns: [] (empty - no beans refreshed)Or:
{
"timestamp": "2026-04-09T10:00:00.000+00:00",
"status": 404,
"error": "Not Found",
"path": "/actuator/refresh"
}Common Causes
- @RefreshScope missing: Bean not annotated for refresh
- Refresh endpoint not exposed: management.endpoints.web.exposure missing refresh
- @Value used instead of @ConfigurationProperties: @Value binds once at startup
- Bean initialized before refresh: Singleton bean cached values at startup
- Refresh event not published: Config client not connected to config server
- EnvironmentChangedEvent not processed: Custom event listener not triggered
Step-by-Step Fix
Step 1: Use @RefreshScope on beans that need refresh
```java import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Service;
@Service @RefreshScope // Bean will be recreated on refresh event public class NotificationService {
@Value("${notification.retry.count:3}") private int retryCount;
@Value("${notification.timeout:5000}") private int timeout;
public void send(String message) { // Uses current values after refresh for (int i = 0; i < retryCount; i++) { try { doSend(message, timeout); break; } catch (Exception e) { // retry } } } } ```
Step 2: Expose refresh endpoint
```yaml # application.yml management: endpoints: web: exposure: include: health,info,refresh,env # Include refresh endpoint
# Spring Cloud Config client spring: config: import: optional:configserver:http://config-server:8888 cloud: config: uri: http://config-server:8888 fail-fast: true retry: initial-interval: 1000 max-interval: 5000 max-attempts: 6 ```
Step 3: Use @ConfigurationProperties with refresh
```java import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component;
@Component @RefreshScope @ConfigurationProperties(prefix = "app") @Data // Lombok public class AppProperties { private String apiEndpoint; private int maxRetries = 3; private Duration timeout = Duration.ofSeconds(5); }
// Usage @Service public class ApiService { private final AppProperties props;
public ApiService(AppProperties props) { this.props = props; // Injected proxy - values update on refresh } } ```
Prevention
- Add @RefreshScope to every bean that reads dynamic configuration
- Expose /actuator/refresh endpoint in management configuration
- Prefer @ConfigurationProperties over @Value for type-safe, refreshable config
- Log EnvironmentChangeEvent to track which properties changed
- Test refresh behavior by modifying config and calling /actuator/refresh
- Use spring-cloud-starter-config for automatic config server integration
- Add config server health check to verify connectivity on startup