Backend Development 10 min read

How Spring Cloud RefreshScope Works: From Annotation to Runtime Refresh

This article explains the inner workings of Spring Cloud's RefreshScope, covering its annotation definition, registration in the auto‑configuration, the refresh endpoint workflow, event handling, and how beans annotated with @ConfigurationProperties or @RefreshScope are dynamically refreshed at runtime.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How Spring Cloud RefreshScope Works: From Annotation to Runtime Refresh

Environment: spring‑cloud‑context 2.2.8.RELEASE + spring‑boot 2.3.9.RELEASE.

1. RefreshScope source code

<code>@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}</code>

The annotation adds

@Scope("refresh")

to mark the scope name as

refresh

.

2. Registering RefreshScope

<code>public class RefreshAutoConfiguration {
    public static final String REFRESH_SCOPE_NAME = "refresh";

    @Bean
    @ConditionalOnMissingBean(RefreshScope.class)
    public static RefreshScope refreshScope() {
        return new RefreshScope();
    }

    @Bean
    @ConditionalOnMissingBean
    public ContextRefresher contextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {
        return new ContextRefresher(context, scope);
    }

    @Bean
    @ConditionalOnMissingBean
    public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) {
        return new RefreshEventListener(contextRefresher);
    }
}</code>

The

RefreshScope

class extends

GenericScope

and implements

ApplicationContextAware

and

ApplicationListener<ContextRefreshedEvent>

. Its core methods are defined in the parent class.

<code>public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        beanFactory.registerScope(this.name, this);
        setSerializationId(beanFactory);
    }
}</code>

During bean retrieval, the registered

RefreshScope

is looked up in

AbstractBeanFactory#doGetBean

, and its

get

method is invoked.

3. Refresh endpoint trigger

<code>@Endpoint(id = "refresh")
public class RefreshEndpoint {
    private ContextRefresher contextRefresher;
    public RefreshEndpoint(ContextRefresher contextRefresher) { this.contextRefresher = contextRefresher; }
    @WriteOperation
    public Collection<String> refresh() { return this.contextRefresher.refresh(); }
}</code>

The endpoint calls

ContextRefresher#refresh

, which first refreshes the environment and then invokes

RefreshScope#refreshAll

.

<code>public class ContextRefresher {
    public synchronized Set<String> refresh() {
        Set<String> keys = refreshEnvironment();
        this.scope.refreshAll();
        return keys;
    }
    public synchronized Set<String> refreshEnvironment() {
        Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());
        addConfigFilesToEnvironment();
        Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
        this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
        return keys;
    }
}</code>

The

EnvironmentChangeEvent

is listened to by

ConfigurationPropertiesRebinder

, which rebinds all beans annotated with

@ConfigurationProperties

:

<code>public class ConfigurationPropertiesRebinder implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {
    @ManagedOperation
    public void rebind() {
        for (String name : this.beans.getBeanNames()) {
            rebind(name);
        }
    }
    public boolean rebind(String name) {
        Object bean = this.applicationContext.getBean(name);
        if (bean != null) {
            this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
            this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);
            return true;
        }
        return false;
    }
}</code>

The supporting auto‑configuration creates

ConfigurationPropertiesBeans

, a

BeanPostProcessor

that collects beans with

@ConfigurationProperties

during initialization.

4. RefreshScope refresh handling

When

RefreshScope#refreshAll

is invoked, it calls the parent

GenericScope#destroy

to clear cached bean instances and then publishes a

RefreshScopeRefreshedEvent

.

<code>public void refreshAll() {
    super.destroy();
    this.context.publishEvent(new RefreshScopeRefreshedEvent());
}</code>

The

GenericScope#destroy

iterates over cached

BeanLifecycleWrapper

objects, acquiring write locks and invoking their

destroy

methods to remove old bean instances.

<code>public void destroy() {
    Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
    for (BeanLifecycleWrapper wrapper : wrappers) {
        Lock lock = this.locks.get(wrapper.getName()).writeLock();
        lock.lock();
        try { wrapper.destroy(); } finally { lock.unlock(); }
    }
    this.errors.clear();
}</code>

After the cache is cleared, the next injection triggers

ObjectFactory#getObject

to create a fresh bean instance.

Summary

Triggering

/actuator/refresh

causes all beans annotated with

@ConfigurationProperties

to be automatically refreshed without needing

@RefreshScope

. The

@RefreshScope

annotation is typically applied to non‑configuration beans that use

@Value

for property injection, allowing their values to be updated dynamically when the refresh endpoint is called.

JavaBackend DevelopmentSpring BootSpring CloudRefreshScopeConfiguration Refresh
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.