Files
spring-boot-demo/core-beans/README.md
T

9.2 KiB

core-beans

Companion project for three articles on ankurm.com.

Article What it demonstrates
Spring Bean Scopes: Singleton, Prototype, Request, Session and the Prototype-in-Singleton Trap instance counts for every scope, five ways to get a fresh prototype inside a singleton, a singleton that leaks one caller's data into another's, and request scope without a proxy failing at start-up
Spring Bean Lifecycle in Boot 4: @PostConstruct, InitializingBean, SmartLifecycle and Shutdown Order every callback in order from a real run, @PostConstruct running before the proxy exists, the non-static BeanPostProcessor warning, SmartLifecycle phases measured, and a request in flight during graceful shutdown with virtual threads
Circular Dependencies in Spring Boot 4: Why Startup Fails and 4 Ways to Fix It the start-up failure report for a three-service ring, the creation order behind it, what allow-circular-references does and does not repair (a half-built bean, @Async versus @Cacheable, an INFO line that swallows an exception), and four fixes run side by side: redesign, events, @Lazy, ObjectProvider

Every console block, exception message and count quoted in those articles came out of output/, and every file there is regenerated by one script. Most are written by the test suite, so if a claim stops being true the build goes red.

There is deliberately no docs/ folder: the deeper material lives in collapsible "going deeper" sections inside the articles themselves, next to the paragraph each one extends.

Versions

Spring Boot 4.1.1
Spring Framework 7.0.9
JDK 25 (Temurin 25.0.4.1+1)
Maven 3.9

Quickstart

export JAVA_HOME=/path/to/jdk-25
mvn test                       # runs the scenarios and rewrites the test-written files in output/
./scripts/run-all.sh           # everything, including the script-captured files

Source layout

Package What it holds
scopes/ singleton, @Lazy, prototype, scoped-prototype and the five ways a singleton can obtain a prototype; the shared-state greeter
web/ @RequestScope, @SessionScope, @ApplicationScope beans and a diagnostic /scopes controller, plus the un-proxied request bean that fails at start-up
lifecycle/ KitchenSink (every callback), the post-processor traps, PhasedWorker and NeverStops for SmartLifecycle
shutdown/ a web application with a /slow endpoint and two SmartLifecycle beans at different phases
cycles/ one package per scenario: broken/ (the three-service ring), allowed/ (field cycles with the flag on), redesign/, events/, lazy/ and provider/ (the four fixes), beanmethods/ (a cycle written as @Bean methods)

Endpoints

Both endpoints exist only to be called by the tests. They have no authorisation. Delete them before shipping.

Endpoint Application Purpose
GET /scopes WebScopesApp request, session and application instance serials, and the injected proxy class
GET /slow?ms=1500 ShutdownApp sleeps, so a request can be in flight when the context closes

Captured output

Files 01-17 and 19-35 (tests) and 18 (capture-metadata.sh). Timing rows assert coarse thresholds, not exact milliseconds; treat them as indicative.

File What it shows
01-instance-counts.txt How many instances did the container really construct?
02-prototype-destroy.txt @PreDestroy on a singleton and on a prototype, then context.close()
03-prototype-in-singleton.txt A singleton calls use() five times. How many prototype instances did it touch?
04-singleton-thread-safety.txt Two threads call the same singleton; a latch forces the interleaving
05-web-scopes.txt request, session and application scope over real HTTP
06-request-scope-without-proxy.txt A request-scoped bean (no proxy) injected into a singleton
07-full-callback-order.txt Every callback for one bean, from constructor to the last destroy hook (SpringApplication, no web server)
08-non-static-bpp-warning.txt A BeanPostProcessor declared with a non-static @Bean method
09-postconstruct-before-proxy.txt @PostConstruct runs before the @Async proxy exists
10-postconstruct-failure.txt An exception thrown from @PostConstruct
11-smartlifecycle-phases.txt Three SmartLifecycle beans registered in the order 300, 100, 200
12-blocking-vs-async-stop.txt Three SmartLifecycle beans in the SAME phase, each needing 400 ms to stop
13-plain-lifecycle.txt Lifecycle vs SmartLifecycle: who starts at refresh()?
14-shutdown-timeout.txt A SmartLifecycle whose stop(callback) never calls the callback, timeout 500 ms
15-smartlifecycle-beans-in-boot.txt Every SmartLifecycle bean in a Boot web application, highest phase (stops first) at the top
16-graceful-shutdown-in-flight.txt A /slow?ms=1500 request is in flight when the context closes
17-worker-phase-vs-web-server.txt SmartLifecycle beans with the default phase and with phase 1000, while a request is in flight
18-property-defaults.txt Property defaults read from spring-configuration-metadata.json (Boot 4.1.1 jars)
19-bean-post-processors.txt The BeanPostProcessors registered in a plain Spring Boot context, in the order they run
20-boot-cycle-failure-report.txt Three constructor-injected services in a ring: what Spring Boot prints when start-up fails
21-cycle-exception-and-creation-order.txt The exception behind the report, and the order in which the container started creating beans
22-bean-method-cycle-failure-report.txt A two-bean cycle written as @Bean methods
23-lazy-initialization-hides-the-cycle.txt spring.main.lazy-initialization=true on the same ring: it starts, then fails on first use
24-allow-circular-half-built-bean.txt A field cycle with the flag off and on: what each @PostConstruct sees
25-allow-circular-async-raw-reference.txt The same cycle with @Async: repaired silently at start-up, a raw-version error under lazy initialisation
26-allow-circular-cacheable-early-proxy.txt The same cycle with @Cacheable: the injected reference is the proxy
27-fix-redesign.txt Fix 1: extract the one thing the second bean needed
28-fix-events.txt Fix 2: an event instead of a call, and what the publisher sees when the listener throws
29-fix-lazy.txt Fix 3: @Lazy on the parameter, and the proxy class that arrives
30-fix-object-provider.txt Fix 4: ObjectProvider, resolving in the constructor, and a final class
31-lazy-on-a-final-class.txt @Lazy on a parameter whose type is a final class
32-lazy-injection-point-versus-lazy-bean.txt @Lazy on the injection point versus on the bean, with a target that cannot be built
33-dispatcher-handler-cycle.txt A cycle through List<Handler> and its ObjectProvider repair
34-depends-on-cycle.txt @DependsOn in both directions: no injection involved
35-lazy-used-during-construction.txt @Lazy on the parameter, but the bean calls the dependency from @PostConstruct
36-main-property-defaults.txt The defaults of spring.main.allow-circular-references and spring.main.lazy-initialization, from Boot's own metadata
37-early-reference-bytecode.txt Which post-processors hand out an early reference, and the BeanCurrentlyInCreationException handler in preInstantiateSingleton

Licence

MIT, see the repository root.