Files

6.5 KiB

core-di

Companion project for two articles on ankurm.com.

Article What it demonstrates
Dependency Injection in Spring Boot 4: Constructor vs Setter vs Field (and Why Field Injection Hurts) the same service in three injection styles built without Spring, injection order, a field-injected dependency used in a constructor, two-constructor ambiguity, circular dependencies in plain Spring vs Boot, and what @Lazy really injects
@Autowired Explained: By-Type Resolution, @Qualifier, @Primary, ObjectProvider and List Injection the real NoUniqueBeanDefinitionException, the resolution ladder read from Spring 7.0.9 bytecode, Optional vs @Nullable vs ObjectProvider, List/Map injection order, and the empty-collection trap

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

Reproduce the Boot start-up failure report by hand:

mvn -DskipTests package
java -jar target/core-di-1.0.0.jar --spring.profiles.active=ctor-cycle
java -jar target/core-di-1.0.0.jar --spring.profiles.active=field-cycle
java -jar target/core-di-1.0.0.jar --spring.profiles.active=field-cycle --spring.main.allow-circular-references=true

Source layout

Package What it holds
injection/ one OrderService written three ways (constructor, setter, field), the trap classes (field used in a constructor, two constructors, circular pairs, @Lazy)
resolution/ two gateways and every way to choose between them (@Primary, @Qualifier, name, @Priority, @Fallback, custom qualifier), optional injection, collections, generics, @Resource
boot/ profile-gated cycle beans for the real "APPLICATION FAILED TO START" report

Captured output

Files 01-18 (tests) and 19-22 (capture-failure-analysis.sh), 23-26 (capture-bytecode.sh). Timing rows assert coarse thresholds, not exact milliseconds; treat them as indicative.

File What it shows
01-three-styles-under-plain-new.txt The same OrderService built with plain 'new', no Spring anywhere
02-fields-and-finality.txt Which injected fields can be final? (reflection over the three variants)
03-injection-order.txt The order Spring touches one bean that uses all three styles
04-field-used-in-constructor.txt A field-injected collaborator used in the constructor
05-multiple-constructors.txt Two constructors: which one does Spring pick?
06-optional-setter.txt @Autowired(required = false) on a setter
07-spring-wires-all-three.txt Inside a Spring context all three styles work
08-circular-plain-vs-boot.txt Constructor cycle vs field cycle: plain Spring and Spring Boot
09-lazy-breaks-constructor-cycle.txt @Lazy on one constructor parameter breaks a constructor cycle
10-no-unique-bean.txt Two PaymentGateway beans and a consumer that asks for one
11-resolution-ladder.txt Who wins when several rules apply at once? (one mini-context per row)
12-missing-bean.txt No PaymentGateway bean at all
13-optional-and-objectprovider.txt Five ways to say 'this may not exist', with zero and with two Notifier beans (Email registered before Sms)
14-list-map-set-injection.txt List, Map and Set injection; registered Push, Email, Sms in that order
15-empty-collection.txt A List injection point when zero Plugin beans exist
16-generics-and-resource.txt Generic type arguments and @Resource
17-dependency-graph.txt getDependenciesForBean: who did Spring wire into whom?
18-parameters-flag.txt The parameter-name fallback needs javac -parameters (compiled twice from the same source)
19-boot-failure-analysis-ctor-cycle.txt Boot start-up failure for a constructor cycle: --spring.profiles.active=ctor-cycle
20-boot-failure-analysis-field-cycle.txt Boot start-up failure for a field-injection cycle: --spring.profiles.active=field-cycle
21-boot-allow-circular-references.txt Same field cycle with --spring.main.allow-circular-references=true
22-boot-lazy-cycle.txt @Lazy on one constructor parameter: --spring.profiles.active=lazy-cycle
23-determine-autowire-candidate-bytecode.txt DefaultListableBeanFactory.determineAutowireCandidate, read with javap
24-early-reference-caches.txt DefaultSingletonBeanRegistry: the fields that hold early references, read with javap
25-spring-nullable-deprecation.txt org.springframework.lang.Nullable in spring-core-7.0.9.jar, read with javap -v
26-boot-parent-parameters-flag.txt spring-boot-starter-parent-4.1.1.pom: the compiler flag that parameter-name matching depends on

Licence

MIT, see the repository root.