skip to content

When exactly are SmartLifecycle beans started during context refresh, and how does isAutoStartup() affect that?

level: seniorimportance: should knowfreq 38%

answer

  1. finishRefresh() -> onRefresh() -> startBeans(true)
  2. autoStartupOnly filter
  3. after singletons + SmartInitializingSingleton
  4. autoStartup=false = manual/context.start() only
  5. stop ignores autoStartup

basics

~10 s

At the end of refresh, DefaultLifecycleProcessor.onRefresh() starts every SmartLifecycle bean whose isAutoStartup() returns true, in ascending phase order. Beans with isAutoStartup()==false are skipped and only start on an explicit start() call.

solid answer

~40 s

During AbstractApplicationContext.refresh(), the final step finishRefresh() invokes the LifecycleProcessor's onRefresh(). DefaultLifecycleProcessor then calls startBeans(autoStartupOnly=true): it collects all Lifecycle beans, filters to those that are SmartLifecycle with isAutoStartup()==true, groups them by phase, and starts phases in ascending order. Plain Lifecycle beans and SmartLifecycle beans with isAutoStartup()==false are excluded here. Setting isAutoStartup() to false is how you opt a SmartLifecycle bean out of automatic startup — it will then only run when something calls ConfigurableApplicationContext.start() (which uses autoStartupOnly=false and starts everything) or when a dependency in the same start sequence triggers it. On context close, stopBeans() stops all lifecycle beans regardless of autoStartup, in descending phase order. This all happens after every singleton is instantiated and after SmartInitializingSingleton callbacks.

code

java · 18 lines
java
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

@Component
class DeferredConsumer implements SmartLifecycle {
    private volatile boolean running;

    // Opt OUT of automatic startup: not started on refresh.
    @Override public boolean isAutoStartup() { return false; }

    @Override public void start() { running = true; /* begin consuming */ }
    @Override public void stop()  { running = false; /* stop, still ordered */ }
    @Override public boolean isRunning() { return running; }
}

// Elsewhere, when ready:
//   ((ConfigurableApplicationContext) ctx).start();  // starts ALL lifecycle beans
// or inject this bean and call start() directly after a runtime condition.

go deeper

for a junior

Know that SmartLifecycle beans start at boot automatically.

for a middle

Should know isAutoStartup() default true and how to opt out.

for a senior

Should place start at finishRefresh()/onRefresh() and describe the autoStartupOnly filter.

for a principal

Reasons about start() vs onRefresh() semantics, lazy interplay, and why lifecycle start is deliberately after full singleton instantiation.

## The exact place in the refresh sequence `AbstractApplicationContext.refresh()` runs a fixed sequence. Near the end: 1. `finishBeanFactoryInitialization()` — instantiates all non-lazy singletons (and fires `SmartInitializingSingleton.afterSingletonsInstantiated()` at the tail of `preInstantiateSingletons()`). 2. `finishRefresh()` — initializes the `LifecycleProcessor` bean (named `lifecycleProcessor`, default `DefaultLifecycleProcessor`) and calls **`getLifecycleProcessor().onRefresh()`**, then publishes `ContextRefreshedEvent`. So **lifecycle start happens after all singletons exist and after SmartInitializingSingleton callbacks** — the whole bean graph is ready before anything is asked to `start()`. ## What onRefresh() actually does `DefaultLifecycleProcessor.onRefresh()` calls `startBeans(true)` — the `true` is **`autoStartupOnly`**. Inside `startBeans`: - It gathers all beans implementing `Lifecycle` from the bean factory. - For each, it includes the bean only if `!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())`. - So with `autoStartupOnly=true`, **only SmartLifecycle beans whose `isAutoStartup()` is true are eligible.** Plain `Lifecycle` beans are excluded; SmartLifecycle beans with `isAutoStartup()==false` are excluded. - Eligible beans are grouped into `LifecycleGroup`s by phase and started in **ascending phase order**. Before starting a bean, if any of its `@DependsOn`/dependency beans are lifecycle beans, those are started first. ## `isAutoStartup()` as an opt-out Override `isAutoStartup()` to return `false` when you want a SmartLifecycle bean that participates in phased **stop** and manual **start**, but should not fire automatically at boot — e.g. a consumer you want to start only after some runtime condition. Such a bean starts when: - `ConfigurableApplicationContext.start()` is called (that path uses `startBeans(false)` and starts *all* lifecycle beans, honoring `isRunning()` to avoid double-start), or - it's pulled in as a lifecycle dependency of a bean being started. ## Shutdown is unconditional w.r.t. autoStartup On `close()`/`stop()`, `stopBeans()` stops **all** lifecycle beans that report `isRunning()==true`, in **descending phase order** — `isAutoStartup()` is irrelevant to stopping. This is why a manually-started (autoStartup=false) bean is still cleanly stopped by the container. ## Gotchas - **Plain `Lifecycle` never auto-starts** — this is the same trap as in the first question, and it's *because* `onRefresh` uses `autoStartupOnly=true`. - **Refresh vs. start:** `context.start()` after refresh will (re)start everything including `autoStartup=false` beans; don't assume `start()` only touches auto-startup beans. - **Lazy beans:** a `@Lazy` SmartLifecycle bean still gets instantiated and started at refresh because the processor asks the factory for all lifecycle beans by type, which forces instantiation. If you truly want deferral, use `isAutoStartup()==false` rather than `@Lazy`.

  • Does context.start() start only auto-startup beans?
    No. ConfigurableApplicationContext.start() calls startBeans(false), which starts every lifecycle bean regardless of isAutoStartup(), skipping only those already isRunning().
  • Where in refresh() does SmartLifecycle start relative to singleton instantiation?
    After. finishBeanFactoryInitialization() instantiates all non-lazy singletons (and runs SmartInitializingSingleton callbacks); finishRefresh() then calls the LifecycleProcessor's onRefresh() to start them.

context