spring-projects/spring-framework
Dependency injection
Active contributors: Juergen Hoeller, Sam Brannen, Sébastien Deleuze
Dependency injection (DI) is the framework's flagship feature. This page traces the complete journey from a class annotated @Component to a fully wired bean ready for use, covering the modules that participate at each step.
Modules involved
| Module | Role in DI |
|---|---|
| spring-core | Type system (ResolvableType), Environment, classpath helpers |
| spring-beans | BeanDefinition, BeanFactory, lifecycle, @Autowired processor |
| spring-context | ApplicationContext, @Configuration, @ComponentScan, @Import |
| spring-aop | Wraps DI'd beans with proxies when AOP is in play |
End-to-end flow
graph TD
USER[Application code: @Configuration, @Component, @Autowired]
USER -->|"new AnnotationConfigApplicationContext(Config.class)"| ACAC[AnnotationConfigApplicationContext]
ACAC -->|"refresh()"| REFRESH[AbstractApplicationContext.refresh]
REFRESH -->|"invokeBeanFactoryPostProcessors"| CCPP[ConfigurationClassPostProcessor]
CCPP -->|"parse @Configuration classes"| BD1["BeanDefinitions for @Bean methods"]
CCPP -->|"@ComponentScan finds @Component classes"| BD2[BeanDefinitions for components]
CCPP -->|"@Import / @ImportSelector / @ImportBeanDefinitionRegistrar"| BD3[Imported BeanDefinitions]
BD1 --> BF[DefaultListableBeanFactory]
BD2 --> BF
BD3 --> BF
REFRESH -->|"finishBeanFactoryInitialization"| INST[Instantiate non-lazy singletons]
INST -->|"per bean"| LC[Bean lifecycle]
LC -->|"AutowiredAnnotationBeanPostProcessor"| INJECT[Inject dependencies]
INJECT -.optional AOP.-> PROXY[Wrap in proxy]
PROXY -->|"register in BeanFactory"| READY[Bean ready]Step 1: Discover bean candidates
Spring discovers bean candidates from four primary sources:
@ComponentScan— Scans configured base packages for classes annotated@Component,@Service,@Repository,@Controller(or any meta-annotated derivative).@Beanmethods on@Configurationclasses — Each@Beanmethod becomes aBeanDefinition.@Importon a configuration class — Registers other configuration classes,ImportSelectorresults, orImportBeanDefinitionRegistraroutputs.- Programmatic registration —
GenericApplicationContext.registerBean(...)orBeanDefinitionRegistry.registerBeanDefinition(...).
The work is orchestrated by ConfigurationClassPostProcessor (in spring-context). It implements BeanDefinitionRegistryPostProcessor and runs early in refresh(), so it shapes the registry before any bean is instantiated.
If META-INF/spring.components is present (generated by spring-context-indexer), CandidateComponentsIndexLoader uses it instead of scanning the classpath — significantly faster for large apps.
Step 2: Build BeanDefinitions
A BeanDefinition carries:
- Class — The bean's runtime type (or factory-method info)
- Scope —
singleton,prototype,request, etc. - Constructor args / property values — How to populate the instance
- Init / destroy callbacks —
@PostConstruct,@PreDestroy, custom names - Dependencies —
@DependsOn-style hints - Qualifiers — From
@Qualifier,@Primary,@Order - Lazy — Defer instantiation until first access
For @Configuration-driven definitions, ConfigurationClassPostProcessor enhances each @Configuration class with a CGLIB subclass that intercepts inter-@Bean-method calls so they return the cached singleton (rather than running the factory method twice).
Step 3: Run BeanFactoryPostProcessors
Several BeanFactoryPostProcessors run after definitions are registered but before any bean is created:
PropertySourcesPlaceholderConfigurer— Resolves${property}placeholders inBeanDefinitionvaluesConfigurationClassPostProcessor— Already covered (it's also aBeanFactoryPostProcessor)- Custom user post-processors — Implement
BeanFactoryPostProcessorand declare as a bean
Step 4: Instantiate non-lazy singletons
DefaultListableBeanFactory.preInstantiateSingletons() walks every singleton BeanDefinition and creates the bean. The lifecycle for one bean:
graph LR
INST[Instantiate via constructor or factory method] --> POP[Populate properties]
POP --> AWARE[Aware callbacks: BeanNameAware, BeanFactoryAware, …]
AWARE --> POSTBEFORE[BPP.postProcessBeforeInitialization: @Autowired, @PostConstruct]
POSTBEFORE --> INIT[afterPropertiesSet, init-method]
INIT --> POSTAFTER[BPP.postProcessAfterInitialization: AOP proxying]
POSTAFTER --> READY[Bean stored in singleton cache]Constructor selection
For constructor injection, Spring picks the constructor as follows:
- If exactly one
@Autowired(required=true)constructor, use it. - If multiple
@Autowiredconstructors, all must berequired=false; Spring tries them in order. - If no
@Autowired, Spring uses the unique non-default constructor if any (since 4.3). - Otherwise the no-arg constructor.
AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(...) is the implementation.
Field / setter injection
AutowiredAnnotationBeanPostProcessor.postProcessProperties(...) walks the bean's class hierarchy collecting @Autowired / @Inject / @Value fields and methods and injects them after construction. It uses DefaultListableBeanFactory.resolveDependency(...) to find the right bean.
Step 5: resolveDependency — the heart of autowiring
Given an injection point (constructor parameter, field, or setter), Spring needs to find the bean(s) that match. The algorithm:
graph TD
REQ[Injection point: ResolvableType + qualifiers] --> CAND[Find candidates by type]
CAND --> QUAL["Filter by @Qualifier (and meta-qualifiers)"]
QUAL --> CHECK{How many candidates?}
CHECK -->|0| FAIL["Fail unless required=false or has @Nullable"]
CHECK -->|1| WIN[Return that bean]
CHECK -->|N| TIE[Tie-breaking]
TIE -->|"@Primary"| PRIMARY[Pick primary]
TIE -->|"javax/jakarta @Priority"| PRIORITY[Pick highest priority]
TIE -->|"otherwise"| AMBIG[NoUniqueBeanDefinitionException]Generic injection
@Autowired private List<EventHandler<UserEvent>> handlers; works because the dependency descriptor preserves generics through ResolvableType. Spring filters candidates by the parameterized generic type, not just the raw class.
Map / List / Provider / Optional injection
| Injection type | Behavior |
|---|---|
List<T> |
All beans of type T, sorted by @Order |
Set<T> |
Same |
Map<String, T> |
Bean name → bean |
T[] |
All beans |
Optional<T> |
Empty if no bean, otherwise the bean |
ObjectProvider<T> / ObjectFactory<T> |
Lazy lookup; supports filtering and ordering |
Provider<T> (jakarta.inject) |
Same as ObjectProvider for basic use |
Step 6: Lifecycle callbacks
After dependencies are injected:
- Aware interfaces —
BeanNameAware.setBeanName,BeanFactoryAware.setBeanFactory,ApplicationContextAware.setApplicationContext, etc. @PostConstructmethods — Invoked once dependencies are wired.InitializingBean.afterPropertiesSet()— Same purpose, older interface-based form.- Custom
init-method— Configured per bean.
On context shutdown, the analogous destroy callbacks fire: @PreDestroy, DisposableBean.destroy, custom destroy-method.
Component scanning details
ComponentScanAnnotationParser and ClassPathBeanDefinitionScanner together implement @ComponentScan. The scanner uses MetadataReader (from spring-core) to read class metadata via ASM without loading the class — this is critical for performance and for avoiding accidental class initialization.
Filters can include or exclude candidates by annotation, by type, by AspectJ pattern, or by custom predicate.
AOT processing
In native-image (GraalVM) builds, classpath scanning is replaced by AOT-generated bean definitions. Spring's AOT processing runs at build time, walks the same configuration classes, and emits Java source code that registers each bean definition explicitly. The runtime uses these generated classes instead of scanning, and reflection hints are pre-registered so the native image keeps everything reachable.
The contributing classes live across:
spring-context/src/main/java/org/springframework/context/aot/— Top-level AOT processorspring-beans/src/main/java/org/springframework/beans/factory/aot/— Bean registration AOT contributorsspring-core/src/main/java/org/springframework/aot/— Foundation hint types
Common pitfalls
- Bean cycles with constructor injection — Spring will throw
BeanCurrentlyInCreationException. Refactor to setter injection, use@Lazy, or break the cycle. - Wrong scope on a singleton-injected prototype — Inject
Provider<T>or use@Lookupto get a fresh prototype each time. - Ambiguous wiring — Two beans of the same type without
@Primaryor@QualifiercauseNoUniqueBeanDefinitionException. - Self-injection for AOP — If you need the proxy of your own bean, inject
ApplicationContextand look up by name, or inject your own type via@Autowired private MyService self;(Spring honors this since 4.3).
See also
- spring-beans — The container's foundation
- spring-context —
ApplicationContextand configuration class processing - aop — How AOP plugs into the lifecycle
- overview/glossary — Vocabulary reference
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.