What is @RefreshScope and what happens when you POST to /actuator/refresh?
answer
- proxy injected, not the real bean
- POST /actuator/refresh -> reload Environment + evict cache
- lazy re-create on next method call
- expose endpoint: exposure.include=refresh
- @Value picks up new values
basics
~10 s@RefreshScope marks a bean so it can pick up new configuration at runtime. Calling POST /actuator/refresh reloads the configuration and recreates those beans, so they see the new property values without restarting the app.
solid answer
~30 s@RefreshScope (from Spring Cloud) puts a bean into a special 'refresh' scope. Instead of a plain singleton, Spring injects a proxy. When you send POST /actuator/refresh, Spring reloads the Environment (re-reads property sources such as application.yml or a Config Server) and clears the refresh-scope cache. The next time anyone calls a method on a refresh-scoped bean, a fresh instance is created and its @Value fields are re-injected with the new values. This lets you change configuration at runtime without restarting the JVM. The endpoint must be exposed via management.endpoints.web.exposure.include=refresh, and spring-cloud-context must be on the classpath.
code
java · 22 lines// build.gradle: implementation 'org.springframework.cloud:spring-cloud-starter'
// application.yml: management.endpoints.web.exposure.include: refresh
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@Component
@RefreshScope
public class GreetingService {
@Value("${app.greeting:hello}")
private String greeting; // re-injected on refresh
public String greet() {
return greeting;
}
}
// 1. curl -X POST http://localhost:8080/actuator/refresh
// -> ["app.greeting"] (the changed keys)
// 2. next call to greet() returns the NEW greetinggo deeper
Know that @RefreshScope + POST /actuator/refresh lets config change at runtime without restart, and the endpoint must be exposed.
Explain the proxy + lazy re-creation and that @ConfigurationProperties don't need @RefreshScope.
Discuss the event sequence and which beans actually pick up values.
Weigh runtime refresh vs restart/redeploy, security of the endpoint, and multi-instance concerns (broadcast lives in the config-bus topic).
**The problem it solves.** Normal Spring beans are singletons created once at startup. Their `@Value("${...}")` fields are set once and never change, so editing a property (locally or on a remote Spring Cloud Config Server) has no effect until you restart. `@RefreshScope` lets specific beans re-read configuration at runtime. **What @RefreshScope is.** `org.springframework.cloud.context.config.annotation.RefreshScope` is a custom Spring bean scope named `"refresh"`, provided by the `spring-cloud-context` dependency. When you annotate a `@Component`/`@Bean` with it, Spring does NOT inject the real object into other beans — it injects a **CGLIB/JDK proxy**. Every method call goes through the proxy, which resolves the *current* target instance from the refresh-scope cache (creating it lazily on first access). **What POST /actuator/refresh does** (handled by `RefreshEndpoint`, which delegates to `ContextRefresher`): 1. Snapshots the current property values. 2. Rebuilds the `Environment` — re-reads all property sources (files, Config Server, env vars). 3. Computes the set of changed keys and publishes an `EnvironmentChangeEvent` carrying those keys. 4. Calls `refreshScope.refreshAll()`, which **evicts every refresh-scoped bean from the cache** and publishes a `RefreshScopeRefreshedEvent`. 5. Returns a JSON array of the property keys that changed. **Lazy re-creation.** Eviction does not immediately rebuild beans. The next method call on the proxy triggers creation of a new instance with fresh dependency injection and fresh `@Value` resolution. Any in-flight call already executing keeps using the old instance until it returns. **Prerequisites / gotchas.** - The endpoint is not exposed by default: add `management.endpoints.web.exposure.include=refresh` (and secure it — it is a state-changing POST). - `@ConfigurationProperties` beans do **not** need `@RefreshScope` — they are rebound automatically (see the follow-up on that). - Refreshing only picks up changes visible to the `Environment`; if a value was captured into a local field during construction and cached elsewhere, it won't propagate. **When to use.** Feature flags, timeouts, sampling rates, log levels, or any tunable you want to change without a redeploy. Avoid it for beans that hold long-lived state or open expensive resources unless you understand that a refresh discards and rebuilds them.
- Does the bean get recreated immediately when you call /actuator/refresh?No. The refresh evicts refresh-scoped beans from the cache but re-creation is lazy — a new instance is built on the next method call through the proxy. Any request already executing finishes on the old instance.
- Why is a proxy needed instead of just swapping the field?Other singletons are wired to the bean once at startup. If they held the real reference, they'd keep the stale instance forever. The proxy is a stable reference that delegates to whichever current instance lives in the refresh-scope cache.
saying these in an interview costs you the question
- Thinking /actuator/refresh restarts the application context or the JVM
- Believing the endpoint is exposed by default (it needs exposure.include=refresh)
- Assuming every bean picks up new @Value automatically without @RefreshScope