Backend Development 7 min read

Implementing Business Data Pre‑warming in Spring Using SmartLifecycle and ContextRefreshedEvent

To pre‑warm business data before a Spring web service accepts requests, the article explains two approaches—implementing the SmartLifecycle interface with proper phase ordering and using a ContextRefreshedEvent listener—while highlighting code examples, execution timing, and cautions against asynchronous overrides.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
Implementing Business Data Pre‑warming in Spring Using SmartLifecycle and ContextRefreshedEvent

When a business needs to pre‑warm data after the Spring container is fully initialized but before the web server can handle requests, two common techniques are used: implementing SmartLifecycle or listening for ContextRefreshedEvent .

1. Implement SmartLifecycle

The SmartLifecycle interface allows precise control over start‑up order by overriding getPhase() . A smaller phase value runs earlier, and it must be less than the phase used by the embedded web server (typically Integer.MAX_VALUE - 1 ).

public class WebServerStartStopLifecycle implements SmartLifecycle {
    private final ServletWebServerApplicationContext applicationContext;
    private final WebServer webServer;
    private volatile boolean running;
    WebServerStartStopLifecycle(ServletWebServerApplicationContext applicationContext, WebServer webServer) {
        this.applicationContext = applicationContext;
        this.webServer = webServer;
    }
    @Override
    public void start() {
        this.webServer.start();
        this.running = true;
        this.applicationContext.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
    }
    @Override
    public void stop() { this.webServer.stop(); }
    @Override
    public boolean isRunning() { return this.running; }
    @Override
    public int getPhase() { return Integer.MAX_VALUE - 1; }
}

In practice a flag such as AtomicBoolean initialized = new AtomicBoolean(false) is used to guarantee the start logic runs only once.

private final AtomicBoolean initialized = new AtomicBoolean(false);
@Override
public void start() {
    if (initialized.compareAndSet(false, true)) {
        // initialization logic
    }
}
@Override
public void stop() { initialized.set(false); }
@Override
public boolean isRunning() { return initialized.get(); }

To ensure the lifecycle runs before the web container starts, return a phase value smaller than the container’s phase (e.g., 0 ).

@Override
public int getPhase() { return 0; }

2. Listen for ContextRefreshedEvent

Another approach is to register a listener for ContextRefreshedEvent . The listener runs after the Spring context is refreshed, which is when the embedded web server has just started.

@Component
public class StartupListener implements ApplicationListener
{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // pre‑warm logic here
    }
}

Do not override the default event multicaster or introduce asynchronous execution, as that may cause the pre‑warm logic to run out of order or fail.

The Spring container initialization sequence (simplified) shows where SmartLifecycle and ContextRefreshedEvent fit: after bean factory post‑processing, listeners are registered, non‑lazy singletons are instantiated, and finally finishRefresh() publishes the refresh event. SmartLifecycle callbacks occur before this event.

public void refresh() {
    synchronized (this.startupShutdownMonitor) {
        // ... preparation steps ...
        invokeBeanFactoryPostProcessors(beanFactory);
        registerBeanPostProcessors(beanFactory);
        initMessageSource();
        initApplicationEventMulticaster();
        onRefresh();
        registerListeners();
        finishBeanFactoryInitialization(beanFactory);
        // Last step: publish corresponding event.
        finishRefresh();
    }
}

Dependencies for the example:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot</artifactId>
    <version>2.6.7</version>
    <scope>compile</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.19</version>
    <scope>compile</scope>
</dependency>

In summary, Spring applications can achieve data pre‑warming either by implementing SmartLifecycle with an appropriate phase or by handling ContextRefreshedEvent , while ensuring the logic runs synchronously and only once.

BackendJavaSpringSmartLifecyclePre-warmingContextRefreshedEvent
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

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.