The Boot Kernel: All Twelve Steps of refresh()

bee2026-10-0834 min read0 views
Behind one line — new AnnotationConfigApplicationContext() — sit twelve precise steps. Walk every step of refresh(), its extension points and its source locations to see what really happens at startup.
1 / 112
Section
0. The 30-second version
2 / 112

A beginner's first reaction to twelve method names is "I cannot memorize this". This article is really about one thing: the single line new AnnotationConfigApplicationContext(...) kicks off a pipeline with a strict order — it first settles which objects will exist, then mounts the hooks that decide how objects get processed, and only at the very end does it start building objects. Get that order wrong and you get the hardest class of bugs: "my AOP quietly stopped working", "startup blew up halfway". So do not memorize twelve names; memorize three time windows.

3 / 112

Terms that recur below, one line each:

4 / 112
  • Container: the "housekeeper" object (ApplicationContext) that creates and manages your objects; you ask it for things with getBean
  • Bean: a plain Java object handed to the container to create and manage
  • BeanDefinition: data describing how to build one bean (class name, singleton or prototype, lazy or not) while the object still does not exist
  • refresh(): the twelve-step routine by which the container turns itself from an empty shell into something ready to serve; defined on AbstractApplicationContext
  • BeanFactoryPostProcessor (BFPP): a processor that rewrites which beans exist — before any object is built. The person who edits the blueprints
  • BeanPostProcessor (BPP): a hook fired once right before and once right after each individual object is born. The person who reworks the finished dish
  • @PostConstruct: a method annotation invoked automatically once the object itself is constructed and its fields filled — the factory self-check
  • Runner (ApplicationRunner / CommandLineRunner): startup tasks invoked only after the container is fully ready
  • Reflection: calling a constructor by name at runtime. The container never binds your class at compile time; it builds objects reflectively from the class name stored in the definition
5 / 112
Analogy

refresh() is the whole process of opening a shop. Picking the site = preparing the Environment (step 1: check water, power and rent — i.e. config and placeholders); drawing the blueprints = producing BeanDefinitions (step 2: plans only, no building yet); running utilities and buying kitchen gear = installing the container's built-ins (steps 3–4); redrawing the plans during renovation = BeanFactoryPostProcessor (step 5: walls can still move, rooms can still be added); etiquette training for staff = BeanPostProcessor registration (step 6: rules must be taught before anyone starts work); installing the till and the PA system = MessageSource and the event machinery (steps 7–10); cooking everything in advance for the soft opening = pre-instantiating all non-lazy singletons (step 11, the slowest); cutting the ribbon = publishing ContextRefreshedEvent (step 12, officially open); opening the doors to customers = running Runners (already outside refresh() — only now can guests walk in).

6 / 112
Diagram
Figure · Chapter map: the twelve steps pinned onto one opening-day timeline
Figure · Chapter map: the twelve steps pinned onto one opening-day timeline
7 / 112
Analogy

two people on this site are the easiest to confuse — the one with the red pen rewriting the blueprints (BeanFactoryPostProcessor, step 5) and the one who reworks a dish already served at the table (BeanPostProcessor, on duty after step 6). The first decides whether a dish is on the menu at all; the second can only decide how it is plated. Section 2 pins that boundary down with a comparison chart.

8 / 112

After this article you should be able to answer three questions:

9 / 112
  • At which step does the @Autowired I wrote actually start to work — and why does it not exist at all during step 5?
  • Which step news a non-lazy singleton, and what happens to everything already built if that step fails?
  • Should my custom logic hang on a BFPP, a BPP, @PostConstruct or a Runner? What can each of them see, and what can they not?
10 / 112
Section
1. Starting from a single main method
11 / 112

Almost every Spring tutorial begins with this one line, yet few stop to ask: how much does it actually trigger?

12 / 112
Code
Codejava
package com.example;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class BootDemo {    public static void main(String[] args) {        // This plain-looking line hides the twelve steps of refresh()        AnnotationConfigApplicationContext ctx =                new AnnotationConfigApplicationContext(AppConfig.class);        HelloService hello = ctx.getBean(HelloService.class);        System.out.println(hello.sayHello("Spring"));        ctx.close();    }}
Notes
  • new AnnotationConfigApplicationContext(AppConfig.class) does just two things in its constructor: register AppConfig as a configuration class, then call refresh() on the last line
  • All the real work lives inside refresh() — the constructor parses no annotations and creates no Bean
  • getBean(...) returns instantly only because refresh() has already pre-instantiated every singleton
13 / 112

The equivalent form is three merged statements: new AnnotationConfigApplicationContext() → register(AppConfig.class) → refresh(). Remember this equality and everything later becomes clearer.

14 / 112
Diagram
Figure 1 · All twelve steps of refresh()
Figure 1 · All twelve steps of refresh()
15 / 112

refresh() belongs to AbstractApplicationContext; stripped of exception handling, its skeleton is:

16 / 112
Code
Codejava
public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        prepareRefresh();                                  // step 1        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 2        prepareBeanFactory(beanFactory);                   // step 3        try {            postProcessBeanFactory(beanFactory);           // step 4            invokeBeanFactoryPostProcessors(beanFactory);  // step 5            registerBeanPostProcessors(beanFactory);       // step 6            initMessageSource();                           // step 7            initApplicationEventMulticaster();             // step 8            onRefresh();                                   // step 9            registerListeners();                           // step 10            finishBeanFactoryInitialization(beanFactory);  // step 11: the main act            finishRefresh();                               // step 12        } catch (BeansException ex) {            destroyBeans();            cancelRefresh(ex);            throw ex;        }    }}
Notes
  • The whole method is wrapped in synchronized, so the same context cannot refresh concurrently
  • No try block covers steps 1–3, so a failure there skips destruction; a failure after step 4 triggers destroyBeans()
  • When reading framework source, skim this skeleton first and then drill down — far more efficient than jumping around blindly
17 / 112
Section
2. The twelve steps, one by one
18 / 112
Animation
Animation · Eight key phases
Animation · Eight key phases
19 / 112

The table below is the heart of this article — worth bookmarking. Each step lists what it does, which method runs it, and where you can hook in:

20 / 112
Table
StepKey methodJob of this stepYour insertion point
1prepareRefreshStamp the start time, mark active, validate required placeholdersApplicationContextInitializer, override initPropertySources
2obtainFreshBeanFactoryCreate a DefaultListableBeanFactory and parse config/XML into BeanDefinitionsBeanDefinitionRegistryPostProcessor can add more definitions
3prepareBeanFactoryInstall built-ins: ClassLoader, ApplicationContextAwareProcessor, ignored dependency interfacesOverride prepareBeanFactory to append ignores
4postProcessBeanFactoryA hook for subclasses; definitions are frozen, no instance exists yetOverride in a subclass (e.g. WebApplicationContext)
5invokeBeanFactoryPostProcessorsRun all BFPPs, parse @Configuration / @Bean / @Import, finalize definitionsBeanFactoryPostProcessor / BeanDefinitionRegistryPostProcessor
6registerBeanPostProcessorsInstantiate and sort all BeanPostProcessorsImplement BeanPostProcessor and make it Ordered
7initMessageSourceRegister the internationalization MessageSource singletonRegister a bean named messageSource
8initApplicationEventMulticasterRegister the event multicaster (a simple synchronous one by default)Register a bean named applicationEventMulticaster
9onRefreshA hook for subclasses; Spring Boot starts the web server hereOverride in a subclass (e.g. ServletWebServerApplicationContext)
10registerListenersRegister ApplicationListeners and dispatch early eventsApplicationListener
11finishBeanFactoryInitializationPre-instantiate all non-lazy singletons — the most expensive stepBeanPostProcessor, SmartInitializingSingleton
12finishRefreshInitialize the lifecycle processor and publish ContextRefreshedEventApplicationListener<ContextRefreshedEvent>
21 / 112
Tip

steps 1–4 "clean the house" (prepare the environment, build the factory, install built-ins); steps 5–6 "set the rules" (definitions and post-processors); steps 7–10 "ready the infrastructure" (events and listeners); and only steps 11–12 actually "produce objects and declare the shop open". Read the twelve steps with this rhythm and they stop being a list of names to memorize.

22 / 112

Twelve names are table-memorization, but order has to be clicked by hand. The debugger below shows the skeleton of refresh() on the left while the right panel refreshes "how many definitions and how many objects exist right now". Press next through all nine cells and watch two numbers in particular: after cell ⑥ singletonObjects is still 0, after cell ⑦ it is still 0 — only cell ⑨ makes it jump.

23 / 112
Stepper
StepperStepping through refresh(): when definitions freeze and when objects are born1 / 9
Step nine times; watch beanDefinitionCount and singletonObjects on the right
Code under debug
1public void refresh() { // AbstractApplicationContext
2 synchronized (startupShutdownMonitor) { // 1. one lock covers both startup and shutdown
3 prepareRefresh(); // 2. step 1: stamp the time, validate placeholders
4 bf = obtainFreshBeanFactory(); // 3. step 2: factory born, definitions enter the table
5 prepareBeanFactory(bf); // 4. step 3: install the built-in kit
6 postProcessBeanFactory(bf); // 5. step 4: the subclass fallback hook
7 invokeBeanFactoryPostProcessors(bf); // 6. step 5: last window to edit definitions
8 registerBeanPostProcessors(bf); // 7. step 6: hooks take their posts
9 ... // 8. steps 7-10: i18n, events, listeners
10 finishBeanFactoryInitialization(bf); // 9. step 11: pre-instantiate every singleton
11 finishRefresh(); // 10. step 12: publish ContextRefreshedEvent
12 }
13}
Variables now
container stateactive = true
beanDefinitionCount0
singletonObjects0
Call stack
1refresh
2prepareRefresh
1Step one does three small jobs: record the start timestamp, flip active to true, validate required placeholders. The third is the one everyone forgets — if some ${...} has no default value and no configuration, it fails right here, not later when a bean is used.
24 / 112

The hardest decision for a beginner is genuinely where my own logic should hang. The sandbox in Section 10 turns the four candidate moments into a single switch — flip one position and it immediately tells you what that step can see and cannot see. Work through it after reading Sections 3 and 4.

25 / 112
Animation
Animation · Eight key phases (quick view of this section)
Animation · Eight key phases (quick view of this section)
26 / 112

The two rows above most often confused are step 5 and step 6, because their English names differ by a single word (both say "post-processor") while they do opposite jobs. Keep them firmly apart:

27 / 112
Analogy

the BFPP at step 5 redraws the blueprints — nothing is built yet, so it can decide how many rooms exist and which one becomes the storeroom. The BPP that takes duty after step 6 reworks an already served dish — it can sprinkle pepper or change the plate (wrap an AOP proxy), but the menu was fixed long ago. To change whether a dish exists you must act during the blueprint phase; to change what a plate looks like you can only act after cooking. That single sentence is the point of the whole article.

28 / 112
Diagram
Figure · Redraw the blueprint versus rework the dish
Figure · Redraw the blueprint versus rework the dish
29 / 112

Now zoom in on one class you wrote: turning from a sheet of paper into the dish on the table crosses four of the twelve steps — 2, 5, 6 and 11. This animation walks that exact route, showing what each step touches: a definition or an object.

30 / 112
Animation
Animation · The path of one bean through refresh
Animation · The path of one bean through refresh
31 / 112
Section
3. A deep dive into four of them
32 / 112
Section
3.1 Step 5: invokeBeanFactoryPostProcessors — the translator of the annotation world
33 / 112

This step is the most underrated. It does far more than "run a few post-processors" — it decides which beans exist at all:

34 / 112
java
// Key logic inside AbstractApplicationContext#invokeBeanFactoryPostProcessors// 1. First run BeanDefinitionRegistryPostProcessors (they may add definitions)//    The heavyweight among them is ConfigurationClassPostProcessor// 2. Then run ordinary BeanFactoryPostProcessors (they only edit definitions)PostProcessorRegistrationDelegate        .invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
35 / 112

ConfigurationClassPostProcessor scans packages named by @ComponentScan → parses @Configuration classes → handles @Bean methods, @Import, @PropertySource → and translates all of it into BeanDefinitions. Whether your annotations take effect is decided here, not in the constructor.

36 / 112

Attention: by the end of this step, the definitions in `beanDefinitionMap` are **essentially final**, and **not a single singleton has been created**. This is the last window to change definitions — and the foothold of auto-configuration.

37 / 112
Section
3.2 Step 6: registerBeanPostProcessors — ordering matters more than registering
38 / 112

BeanPostProcessors (BPPs) wrap every bean's initialization, and their order decides the sequencing of AOP proxies, @Autowired resolution and @PostConstruct. Spring therefore grades them carefully:

39 / 112
Code
Codejava
// Precedence: PriorityOrdered > Ordered > unordered (in registration order)// The result is split into two groups: ordinary BPPs and MergedBeanDefinitionPostProcessors// The former register first; the latter are called back to rewrite merged definitions at pre-instantiation
Notes
  • BPPs implementing PriorityOrdered register first
  • Those implementing Ordered come next
  • The rest come last, in indeterminate order — so a custom BPP must implement Ordered if its order matters

Trap: a custom BeanPostProcessor that does not implement Ordered may interact with other BPPs in an order opposite to what you expect (for example, fetching an already-proxied bean inside your BPP), producing a "sometimes right, sometimes wrong" bug.

40 / 112
Section
3.3 Step 11: finishBeanFactoryInitialization — the real main act
41 / 112
java
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {    // e.g. prepare a ConversionService early for @Autowired    if (beanFactory.containsBeanDefinition(CONVERSION_SERVICE_BEAN_NAME)) { /* ... */ }    // Freeze all definitions; no further changes allowed    beanFactory.freezeConfiguration();    // The core: pre-instantiate every non-lazy singleton    beanFactory.preInstantiateSingletons();}
42 / 112

preInstantiateSingletons() iterates over every beanDefinitionNames, skips abstract, non-singleton and lazy-init=true definitions, and calls getBean(name) for the rest. Every "constructor invoked, @PostConstruct executed, AOP proxy created" you ever see in business code happens here. After the loop, it also calls back every bean implementing SmartInitializingSingleton.

43 / 112
Section
3.4 Step 12: finishRefresh — declaring the shop open
44 / 112
Code
Codejava
protected void finishRefresh() {    clearResourceCaches();                    // clear caches    initLifecycleProcessor();                 // lifecycle processor ready    getLifecycleProcessor().onRefresh();      // start components implementing Lifecycle    publishEvent(new ContextRefreshedEvent(this)); // the key: publish the refresh event}
Notes
  • ContextRefreshedEvent is the most widely used startup hook: cache warm-up, connection-pool init and one-time data loading can all listen to it
  • Be careful though: this event is published multiple times (child contexts publish it too), so always check whether event.getApplicationContext() is the one you care about
  • The start() of Lifecycle components also fires here — in Spring Boot the embedded Tomcat startup order is related to this
45 / 112
Section
4. Matching the startup log to each step
46 / 112

Set logging.level.org.springframework=DEBUG and you will see the log below. I have tagged each line with its step:

47 / 112
Code
Codetext
# Step 1 · prepareRefresh2024-06-01 17:02:11.301 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a2b3c4d# Step 5 · invokeBeanFactoryPostProcessors (ConfigurationClassPostProcessor at work)2024-06-01 17:02:11.412 [main] INFO  o.s.c.a.ConfigurationClassPostProcessor - Cannot enhance @Configuration bean definition 'AppConfig' since its singleton instance has been created too early# Step 6 · registerBeanPostProcessors2024-06-01 17:02:11.470 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'# Step 11 · finishBeanFactoryInitialization (many singletons are new-ed here)2024-06-01 17:02:11.520 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloService'2024-06-01 17:02:11.523 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'# Step 12 · finishRefresh2024-06-01 17:02:11.589 [main] DEBUG o.s.c.e.EventPublicationInterceptor - ...2024-06-01 17:02:11.610 [main] INFO  o.s.c.a.AnnotationConfigApplicationContext - Started BootDemo in 0.312 seconds
Notes
  • Refreshing ... is the opener printed at step 1; Started ... in x seconds is the closer at step 12 — that short span is the whole startup
  • The flood of Creating shared instance of singleton bean 'xxx' clusters in step 11, and this segment usually accounts for 80%+ of startup time
  • A "created too early" warning almost always means you fell into the trap in Section 7

Key point: to tune startup speed, sort the step-11 Creating shared instance lines by duration — that is the most direct optimization checklist. Whatever should not be created at startup can be deferred with @Lazy.

48 / 112
Section
5. What happens when refresh fails
49 / 112

The catch block of refresh() is short but heavy with meaning:

50 / 112
Code
Codejava
catch (BeansException ex) {    destroyBeans();       // destroy every singleton created so far — no half-baked leaks    cancelRefresh(ex);    // clear the active flag and reset the context state    throw ex;             // rethrow so the caller knows startup failed}
Notes
  • destroyBeans() walks the registered singletons and invokes their destroy callbacks — a graceful wrap-up even on failure
  • cancelRefresh() resets active to false so the context may be refreshed again
  • Note: the thrown exception is usually a BeanCreationException, with the real cause buried in getMostSpecificCause()
51 / 112

Spring Boot layers a FailureAnalyzer on top, turning ugly stack traces into plain language:

52 / 112
Code
Codejava
public class MyPortFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {    @Override    protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) {        return new FailureAnalysis(            "Port already in use, cannot start the server: " + cause.getMessage(),            "Change server.port or kill the process holding the port.",            cause);    }}
Notes
  • Register it via META-INF/spring.factories (Boot 3 uses META-INF/spring/org.springframework.boot.diagnostics.FailureAnalyzer.imports)
  • Boot ships over a dozen analyzers (port conflict, missing DataSource, absent config...), which turn "startup failure" from torture into a hint
53 / 112

Those cleanup steps run in a fixed order. The animation plays in a few seconds, but it decides whether a production incident reads as "failed cleanly" or "left a half-built container behind":

54 / 112
Animation
Animation · Five moves when refresh fails
Animation · Five moves when refresh fails
55 / 112

The lifecycle processor installed at step twelve also runs the shutdown in reverse: when the context closes, each phase (SmartLifecycle groups by role) has to stop inside its time budget. Drag that number once and its relationship with the orchestrator becomes concrete:

56 / 112
Tuner
TunerHow long a shutdown phase may take: more waiting is not safer
spring.lifecycle.timeout-per-shutdown-phase
30secondsNow 0 – 120
Default: lines up with the K8s grace period
  • Boot defaults to 30s and Kubernetes terminationGracePeriodSeconds also defaults to 30s
  • Do not enable server.shutdown=graceful and then add 25s of business cleanup — the two budgets squeeze each other
  • Past the deadline you get one log line, not an exception
In-flight requests cut8%
Shutdown duration60%
The real opponent of this knob is not your business duration but the seconds the orchestrator grants you — both sides must be configured together.
57 / 112
Warning

never try { refresh(); } catch (Exception e) { / ignore / }. A failed startup leaves the container half-initialized; using it further will plant hard-to-diagnose null pointers.

58 / 112
Section
6. Interview follow-ups: two frequent questions
59 / 112

Q1: Why is refresh() guarded by synchronized?

60 / 112
Code
Codejava
// AbstractApplicationContextprivate final Object startupShutdownMonitor = new Object();public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) { /* ... */ }}
Notes
  • refresh() and close() share the same lock, preventing a race between "starting up" and "shutting down"
  • It also guarantees the same context is never refreshed twice concurrently, which would duplicate singletons and events
  • Note the lock is method-level serialization, unrelated to business threads; once startup finishes it no longer applies
61 / 112

Q2: How do SpringApplication.run() and refresh() relate?

62 / 112
  • run() is the Spring Boot facade: prepare the Environment → print the Banner → create the ApplicationContext → call refreshContext() (which is refresh()) → run ApplicationRunner / CommandLineRunner
  • In other words, refresh() is just one key sub-step of run(); run() also prepares the environment, wires listeners, prints the Banner and runs runner callbacks
  • One sentence to remember: refresh() governs "inside the container"; run() governs "assembling the whole application"
63 / 112
Table
Dimensionrefresh()SpringApplication.run()
Owning classAbstractApplicationContextSpringApplication
Main jobRefresh the container: definitions, post-processors, pre-instantiationAssemble the app: environment, Banner, listeners, runners
ContainmentCalled by refreshContext()Calls refresh() internally
Exception semanticsThrows BeansException (e.g. BeanCreationException)May aggregate into IllegalStateException or rethrow
64 / 112
Section
7. Two traps: misusing an extension point can break things
65 / 112
Trap

calling getBean() inside a BeanFactoryPostProcessor triggers early instantiation. Many people want to "check whether a bean can be created" inside a BFPP and write beanFactory.getBean("userService") — this instantly instantiates userService while no BeanPostProcessor is registered yet, so it gets no AOP proxy and @Autowired is never processed, yielding a crippled bean. The correct approach is to operate on BeanDefinitions, not objects.

66 / 112
Trap

timing of manual setXxx before refresh(). Calls like ctx.setParent(...), ctx.register(...), registerBeanDefinition(...) must happen before refresh(), otherwise they throw IllegalStateException (the container is active). After step 11 completes, singletons are pooled and definitions frozen — too late to change anything.

67 / 112
Section
8. Try it: watch the container assemble itself
68 / 112

The demo below visualizes "how each definition changes state in the Beans list". Match it against steps 5–11 to see which beans each step pushes to "created":

69 / 112
Kernel lab
70 / 112
Section
9. At which step should a custom extension point hang?
71 / 112
Decision
Decisionyou need a single round of data initialization at the moment "every bean has been created but the container has not yet published its startup-complete event". Where should this logic hang?
72 / 112
Section
10. Labs: run the twelve steps end to end
73 / 112

Memorizing method names does not work; making them move does. The four labs below are ordered "main line → hooks → the definition table → outside the container", and each needs only the parameter switch in the corner.

74 / 112

The first lab is the main line. Pick "All twelve" and watch them light up one by one, noticing which ones are marked as critical; then switch to "The four that matter" and you will find that invokeBeanFactoryPostProcessors, registerBeanPostProcessors, finishBeanFactoryInitialization and finishRefresh decide 90% of the behaviour, while the other eight merely glue them together:

75 / 112
Kernel lab
TeaVMThe twelve steps of refresh(), lighting up one by oneidle
Start with 'All twelve' for the order, then switch to 'The four that matter' to grab the backbone
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
76 / 112

The second lab targets the place beginners most often go wrong — the timing of the hooks. Pick "Post-processors" and watch steps 3 and 4: once a bean is born early because a BPP depended on it, it escapes processing for life, which is exactly where the log line is not eligible for getting processed by all BeanPostProcessors comes from:

77 / 112
Kernel lab
TeaVMWhy hooks have to be on duty firstidle
Focus on steps 3 and 4: a bean born early carries its 'never enhanced' badge for life
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
78 / 112

The third lab answers "where does the blueprint actually live?". Pick "The registry" and see that beanDefinitionMap holds only descriptions while singletonObjects is still empty — the root of the trap in Section 7, where calling getBean() at the definition stage is queue-jumping:

79 / 112
Kernel lab
TeaVMAt the end of step 5, what the registry has and lacksidle
Compare the two tables: beanDefinitionMap already holds dozens of entries while singletonObjects is still empty
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
80 / 112

The fourth lab zooms out: refresh() is only step ④ of SpringApplication.run(). Pick "The eight steps" against the opening-day timeline, then switch to "Runners" to confirm your own startup logic runs only after the ribbon — which directly determines which row of the sandbox in Section 11 you should pick:

81 / 112
Kernel lab
TeaVMBeyond refresh: the site, the ribbon and the open dooridle
Use 'The eight steps' to locate refreshContext, then 'Runners' to see when your logic actually starts
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
82 / 112

Inside the batch of hooks registered at step six sits the one that decides whether a bean deserves a proxy — the actual gate for AOP. Switch to "Interception in BPP" and watch it question every candidate in turn; that is also why the Section 7 line not eligible for getting processed by all BeanPostProcessors really means "this object escaped proxying":

83 / 112
Kernel lab
TeaVMWhich hook from step 6 wraps the proxyidle
Go through the candidate check and the proxy factory; line both up against the silent-failure trap in Section 7
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
84 / 112

And when step eleven actually births a bean, it follows a fixed eight-station pipeline. This lab expands the part after preInstantiateSingletons — run the singleton route first, then "Watch destroy" to see exactly whom destroyBeans() from Section 5 calls:

85 / 112
Kernel lab
TeaVMInside step 11: how one bean runs through its whole lifeidle
Focus on the destroy phase: these callbacks still run when refresh fails — that is the entirety of 'cleaning up politely'
Scenario
Click “Run demo” to execute the AOT-compiled Java kernel right in your browser, step by step.
86 / 112
Section
10.1 Same content from the command line: walk the twelve steps in order
87 / 112

Enough pressing buttons — type the commands yourself. This console talks to the real container executing in your browser, and every response from beans, conditions and query is computed by the kernel. Run them in this order and you are stepping through refresh() itself:

88 / 112
Console
89 / 112
Tip

type beans immediately after boot. The list contains your business beans and a crowd of names starting with org.springframework. — those are the standard fittings installed at step three ("running the utilities"). When you cannot tell yours from the framework's, the prefix is the fastest filter.

90 / 112
Section
11. Sandbox: what your logic can see at each hook point
91 / 112

The beginner's pain here is very concrete: for one requirement, the number of objects visible differs wildly across four different hook points. At step 5 there is not a single instance; at Runner time it is arguably too late (the embedded container is already taking traffic). The sandbox below turns the four candidates into one switch — flip one position and you immediately see that step's field of view and its danger:

92 / 112
Sandbox
SandboxHook-point sandbox: what your logic can see
Result
Position: refresh() step 5, invokeBeanFactoryPostProcessors
Sees: every definition in beanDefinitionMap (about 40), Environment, ${} placeholders
Cannot see: any business object — getBean("userService") forces it into existence
Dangerous move: fetching a bean here makes it escape all later BPP processing
Good for: bulk edits to scope / lazy, injecting extra definitions, swapping implementations
Blueprint phase: you may move walls, but you may not summon staff. That is the most important sentence in this article — to change whether something exists, do it here.
93 / 112
Tip

work through all four positions in sequence and the only dividing line you will find is "has the object been born yet". Step 5 has blueprints, step 6 has hooks, step 11 has finished objects, and the Runner has the whole shop. Selection mnemonics: change whether it exists → BFPP; change what it looks like → BPP; only mind yourself → @PostConstruct; need everything ready → Runner.

94 / 112
Section
12. Quick quizzes
95 / 112

A warm-up question, whose answer sits in the refresh lab of Section 10:

96 / 112
Quiz
Check yourselfWhich step of `refresh()` actually `new`s a non-`@Lazy` singleton bean?
Pick one — you get feedback right away
97 / 112

The heavy one, matching Section 3.2 and the second row of the Section 11 sandbox:

98 / 112
Quiz
Check yourselfWhy must `BeanPostProcessor`s be registered in `registerBeanPostProcessors()` (step 6) rather than later, when a business bean needs them?
Pick one — you get feedback right away
99 / 112
Section
13. Common errors cheat sheet
100 / 112

Every "error text" row below can be copied verbatim into a search engine. They share one trait: the exception tells you which step failed, while the disease is written in the last line of the caused by chain.

101 / 112
Table
Error text (fragment)Real cause30-second fixDeep dive
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [com/example/AppConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is ... NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.OrderRepository' availableDuring step 11 pre-instantiation, a singleton's dependency is absent from the registry. Usually that class lacks @Service / @Component, or its package is outside the @ComponentScan rangeThere is exactly one way to read it: start from the last Caused by and work outwards — the root cause is always innermost. The outer chain of Error creating bean with name is only collateral damage. Search Error creating bean with name in your IDE and the first hit is the real culpritSection 5 · Article 7 BeanDefinition
Web server failed to start. Port 8080 was already in use. (the underlying exception is org.springframework.boot.web.server.PortInUseException)The embedded Tomcat started during step 12 finishRefresh and found the port taken. Thousands of beans were already built before the failure, which makes it look half-successful when in fact everything is discardedBoot already translates it into APPLICATION FAILED TO START plus an ACTION paragraph. Find the holder: `netstat -ano \findstr :8080 on Windows, lsof -i :8080 on macOS/Linux; use server.port=0` for a random port as a temporary dodgeSection 4 log mapping · Article 5 your first project
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'helloService' defined in URL [file [./beans.xml]]: There is already [Generic bean: class [com.example.HelloServiceImpl]] bound.The same beanName was registered twice with incompatible definitions. Since Boot 2.1 spring.main.allow-bean-definition-overriding defaults to false, so step 5 rejects it outrightNine times out of ten it is XML mixed with annotations, or two overlapping @ComponentScan ranges scanning the same family twice. Read only two parts of the error: the conflicting name and the two sources after defined in. If you truly need the override, open the flag explicitly (not recommended long-term)The trap in Section 7 · Article 7 BeanDefinition
java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@... has been closed already (on a second refresh() of the same context you first see Exception encountered during context initialization - cancelling refresh attempt: ... in the log, and fetching a bean reports ... has not been refreshed yet)You operated on the same context outside the legal window: calling getBean after close(), or calling register(...) + refresh() twice while hand-rolling a container. The startupShutdownMonitor lock prevents concurrency, not repetitionKeep the one-shot flow in your head: new → register → refresh() → use → close(), each exactly once. For a clean state, build a new context; in tests let @SpringBootTest own the lifecycle instead of calling refresh() yourselfQ1 in Section 6
No qualifying bean of type 'com.example.Notifier' available: expected single matching bean but found 2: smsNotifier,mailNotifier (what is actually thrown is NoUniqueBeanDefinitionException, a subclass of NoSuchBeanDefinitionException)The definition stage is fine; ambiguity appears at type-based resolution: two candidates of the same type and no disambiguation hint at the injection pointThree fixes in priority order: add @Qualifier("smsNotifier") at the injection point; mark the preferred one @Primary; if you really want both, declare List<Notifier> and let the container inject bothThe bd lab in Section 10 · Article 6 IoC and DI
The one with no exception at all: the startup log shows Bean 'dataSource' of type [...] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying), and @Transactional / your custom aspect on that bean then quietly stops workingYour custom BeanPostProcessor depends on an ordinary bean (or injects a DataSource directly). To satisfy that BPP, the bean was forced into existence before step 6, so it skipped postProcessAfterInitialization and was never wrapped in a proxyPick any of three fixes: inject ObjectProvider<T> or a @Lazy proxy into the BPP; implement BeanFactoryAware and fetch it later; hand the proxying job back to the AOP side. Always implement Ordered on a custom BPP, or order problems pile onSection 3.2 · the second row of the Section 11 sandbox
102 / 112
Warning

the classic beginner mistake with these long stack traces is to grind top-down. The right posture is to read only three lines: ① the outermost exception type (which step blew up); ② the first Error creating bean with name 'xxx' (which object failed); ③ the last Caused by: (the actual disease). The dozens of lines in between are the same kind of collateral and skipping them changes nothing.

103 / 112

That reading method needs a live case. Below is the most common step-eleven crash — do not check the table, click the frame you believe is guilty:

104 / 112
Triage
Error triageBeanCreationException

A teammate adds one repository class. Local startup now fails, the console scrolls for most of a screen, and he spends ten minutes reading from the top without understanding a line of it.

APPLICATION FAILED TO START
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [com/example/AppConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.OrderRepository' available: expected at least 1 bean which qualifies as autowire candidate
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentNames(ConstructorResolver.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1375)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1219)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:591)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.OrderRepository' available
... 34 more
Click the frame you blame — guessing is allowed
No pressure: guess the exception first, then which line actually made the call.
105 / 112
Section
14. Self-check
106 / 112
Self-check

without looking back, compress the twelve steps into three time windows and say what problem each window solves.

107 / 112
Self-check

Which step creates non-lazy singletons? How many business objects are in singletonObjects before that step?

108 / 112
Self-check

Which interface do you implement to change "which beans exist", and at which step? Which one changes "what an object looks like", and when does it take effect?

109 / 112
Self-check

What goes wrong if a custom BeanPostProcessor injects an ordinary bean? Which log line is its fingerprint?

110 / 112
Self-check

When step 11 throws, where do the beans already built go? Why is it "all or nothing"?

111 / 112
Mnemonic

one to four lay the foundation (environment, factory, built-ins), five and six set the rules (redraw plans, train staff), seven to ten ready the equipment (messages, broadcast, empty hook, listeners), eleven is where the food comes out (pre-instantiation), twelve cuts the ribbon (publishes the event) — and opening the doors to customers is the Runner, already outside refresh.

112 / 112
Summary

refresh() is not a black box but twelve clearly-scoped steps. Remember three groupings — steps 1–4 prepare the environment and factory, steps 5–6 process definitions and register post-processors, steps 7–12 ready the infrastructure and pre-instantiate singletons; remember two windows — BeanFactoryPostProcessor edits definitions (step 5), BeanPostProcessor edits instances (after step 6); and remember one finish line — step 12 publishes ContextRefreshedEvent. Once you can read these twelve steps, startup holds no more secrets.