skip to content

@RefreshScope & /actuator/refresh

@RefreshScope beans are proxies that are discarded and rebuilt when /actuator/refresh fires, so new property values take effect without a restart. Interviewers ask which beans actually pick up the change, because plain @Value fields in singletons do not.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is @RefreshScope and what happens when you POST to /actuator/refresh?

level: juniorimportance: must knowfreq 70%

answer

  1. proxy injected, not the real bean
  2. POST /actuator/refresh -> reload Environment + evict cache
  3. lazy re-create on next method call
  4. expose endpoint: exposure.include=refresh
  5. @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
java
// 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 greeting

go deeper

for a junior

Know that @RefreshScope + POST /actuator/refresh lets config change at runtime without restart, and the endpoint must be exposed.

for a middle

Explain the proxy + lazy re-creation and that @ConfigurationProperties don't need @RefreshScope.

for a senior

Discuss the event sequence and which beans actually pick up values.

for a principal

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

context

open as a page

On /actuator/refresh, which beans pick up new values — @Value beans vs @ConfigurationProperties beans — and why?

level: middleimportance: must knowfreq 60%

basics

~10 s

@ConfigurationProperties beans are automatically rebound with new values on refresh, no annotation needed. A bean using @Value only picks up new values if you also put @RefreshScope on it.

open as a page

Walk through the events fired by ContextRefresher during /actuator/refresh: EnvironmentChangeEvent vs RefreshScopeRefreshedEvent — what each carries and their order.

level: seniorimportance: should knowfreq 40%

basics

~20 s

First the Environment is reloaded and an EnvironmentChangeEvent is published carrying the set of changed property keys. Then the refresh scope is cleared and a RefreshScopeRefreshedEvent is published to signal that refresh-scoped beans were evicted.

open as a page

What are the concurrency and lifecycle gotchas of @RefreshScope beans — in-flight requests, @PostConstruct, and beans holding expensive resources?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A refresh evicts the bean but recreation is lazy, so a request already running keeps the old instance. On recreation, the old bean's @PreDestroy runs and the new bean's @PostConstruct runs again. Beans that open connections or pools get torn down and rebuilt, which can be costly.

open as a page

As an architect, how do you decide between @RefreshScope runtime refresh, plain @ConfigurationProperties rebind, and a full restart/redeploy for a config change — and what are the tradeoffs and risks?

level: principalimportance: should knowfreq 25%

basics

~20 s

Use @ConfigurationProperties rebind for simple tunables that change in place; add @RefreshScope only when a bean must fully rebuild on config change. Prefer a restart/redeploy for structural or safety-critical changes, since runtime refresh has weaker guarantees and audit trail.

open as a page