Which component processes @Autowired, and at what point in the bean lifecycle does constructor vs field/setter injection happen?
answer
- AutowiredAnnotationBeanPostProcessor
- also does @Value + @Inject
- constructor = during instantiation
- field/setter = populateBean/postProcessProperties
- all set before @PostConstruct
basics
~10 sAutowiredAnnotationBeanPostProcessor handles @Autowired (and @Value and @Inject). Constructor injection is resolved when the bean is instantiated; field and setter injection happen afterward during the property-population phase, before initialization callbacks.
solid answer
~40 s@Autowired is processed by AutowiredAnnotationBeanPostProcessor, an infrastructure BeanPostProcessor registered automatically by component scanning or annotation-config. It implements SmartInstantiationAwareBeanPostProcessor. During bean creation, constructor injection is resolved first: the processor's determineCandidateConstructors picks the constructor and Spring resolves its argument beans to instantiate the object. Field and setter injection come later in the populateBean phase via postProcessProperties, which walks cached InjectionMetadata and calls DefaultListableBeanFactory.resolveDependency for each point. All of this runs before init callbacks like @PostConstruct and InitializingBean.afterPropertiesSet, so injected collaborators are available there. A practical consequence: constructor-injected dependencies exist before the object does, enabling final fields and eliminating a class of circular-dependency and null-during-init bugs that field injection can hide.
code
java · 18 lines@Component
class OrderProcessor implements InitializingBean {
private final PricingService pricing; // constructor-injected at instantiation
@Autowired private AuditLogger audit; // field-injected during populateBean
OrderProcessor(PricingService pricing) { // resolved before object exists
this.pricing = pricing;
}
@Override public void afterPropertiesSet() {
// Runs AFTER populateBean -> both pricing and audit are non-null here.
audit.record("OrderProcessor ready with " + pricing);
}
}
// AutowiredAnnotationBeanPostProcessor:
// - determineCandidateConstructors() -> picks the sole constructor
// - postProcessProperties() -> resolves the @Autowired audit fieldgo deeper
Just know a bean post-processor does the wiring automatically.
Name AutowiredAnnotationBeanPostProcessor and that init runs after injection.
Distinguish instantiation-time constructor injection from populateBean field/setter injection and cite resolveDependency.
Connect lifecycle ordering to final fields, circular-dependency semantics, and injection-metadata caching.
### The processor `@Autowired` is not magic in the class loader — it is applied by **`AutowiredAnnotationBeanPostProcessor`** (package `org.springframework.beans.factory.annotation`). The **same** processor also handles **`@Value`** and JSR-330 **`@Inject`**. It is registered automatically by: - component scanning (`@ComponentScan` / Spring Boot), or - `<context:annotation-config/>` / `AnnotationConfigApplicationContext`. It implements **`MergedBeanDefinitionPostProcessor`** (to scan and cache injection metadata) and **`SmartInstantiationAwareBeanPostProcessor`** (to choose constructors and populate properties). ### Where in the lifecycle each style runs Spring creates a bean in `AbstractAutowireCapableBeanFactory.doCreateBean`, roughly: 1. **Determine constructor** — `SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors` lets the processor pick the `@Autowired` (or sole) constructor. 2. **Instantiate** — Spring resolves each **constructor argument** as a dependency and calls the constructor. So **constructor injection happens *during* instantiation** — the object never exists uninitialized. 3. **populateBean** — for **fields and setters**, `postProcessProperties` runs. The processor reads cached **`InjectionMetadata`** (the list of annotated fields/methods) and for each calls **`DefaultListableBeanFactory.resolveDependency(...)`** to find and inject the value. 4. **Initialization** — Aware callbacks, `@PostConstruct`, `InitializingBean.afterPropertiesSet`, then custom init. All injected dependencies (constructor *and* field/setter) are set **before** these run. ### Injection metadata & performance The processor scans a class **once** and caches an `InjectionMetadata` object keyed by bean name/class, so reflection cost isn't repaid per instance. Fields/methods are made accessible and invoked reflectively. ### Why the ordering matters - **Final fields**: only constructor injection can set `final` fields, because it runs at instantiation. - **Circular dependencies**: two beans that constructor-inject each other cannot be built (chicken-and-egg) and Spring throws `BeanCurrentlyInCreationException`; the same cycle *may* resolve with field/setter injection because the half-built bean can be exposed early. This is a reason field injection can mask design smells. - **`@PostConstruct` sees everything**: because init runs after populateBean, you can safely use any injected collaborator there. ### Gotchas - The processor must be present. In a hand-rolled `GenericApplicationContext` without annotation-config, `@Autowired` is silently ignored. - `@Autowired` on a **static** field/method is **not** supported. - Ordering between multiple `BeanPostProcessor`s is governed by `Ordered`; the autowired processor runs at a well-defined infrastructure order. ### When to reason about this When debugging null injected fields (missing processor, static target), circular-dependency errors, or when explaining why constructor injection is safer than field injection.
- Why can constructor injection cause a circular-dependency failure that field injection doesn't?Constructor args must be resolved before the object exists, so a mutual constructor cycle is unbuildable and throws BeanCurrentlyInCreationException. Field/setter injection populates after instantiation, letting Spring expose a half-built singleton early to break the cycle.
- What happens to @Autowired if the processor isn't registered?Nothing — the annotation is ignored and fields stay null. Component scanning, @EnableXxx configs, or context:annotation-config register AutowiredAnnotationBeanPostProcessor; without it there is no one to act on @Autowired.
saying these in an interview costs you the question
- Claiming field injection happens inside the constructor
- Saying @PostConstruct may run before dependencies are injected
- Not knowing the same processor handles @Value and @Inject
- Thinking @Autowired works on static fields