Open-Source Wikis

/

Spring Framework

/

Features

/

Dependency injection

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:

  1. @ComponentScan — Scans configured base packages for classes annotated @Component, @Service, @Repository, @Controller (or any meta-annotated derivative).
  2. @Bean methods on @Configuration classes — Each @Bean method becomes a BeanDefinition.
  3. @Import on a configuration class — Registers other configuration classes, ImportSelector results, or ImportBeanDefinitionRegistrar outputs.
  4. Programmatic registrationGenericApplicationContext.registerBean(...) or BeanDefinitionRegistry.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)
  • Scopesingleton, 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 in BeanDefinition values
  • ConfigurationClassPostProcessor — Already covered (it's also a BeanFactoryPostProcessor)
  • Custom user post-processors — Implement BeanFactoryPostProcessor and 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:

  1. If exactly one @Autowired(required=true) constructor, use it.
  2. If multiple @Autowired constructors, all must be required=false; Spring tries them in order.
  3. If no @Autowired, Spring uses the unique non-default constructor if any (since 4.3).
  4. 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:

  1. Aware interfacesBeanNameAware.setBeanName, BeanFactoryAware.setBeanFactory, ApplicationContextAware.setApplicationContext, etc.
  2. @PostConstruct methods — Invoked once dependencies are wired.
  3. InitializingBean.afterPropertiesSet() — Same purpose, older interface-based form.
  4. 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 processor
  • spring-beans/src/main/java/org/springframework/beans/factory/aot/ — Bean registration AOT contributors
  • spring-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 @Lookup to get a fresh prototype each time.
  • Ambiguous wiring — Two beans of the same type without @Primary or @Qualifier cause NoUniqueBeanDefinitionException.
  • Self-injection for AOP — If you need the proxy of your own bean, inject ApplicationContext and look up by name, or inject your own type via @Autowired private MyService self; (Spring honors this since 4.3).

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Dependency injection – Spring Framework wiki | Factory