How @Autowired Works Internally
Fast recall
Quick revision
Core answer
BeanPostProcessor Spring registers automatically. It uses Java reflection to scan each bean's fields, setters, and constructors for the annotation, then queries the BeanFactory to resolve matching beans by type → `@Qualifier` → `@Primary` → field name. For constructor injection, Spring resolves dependencies before instantiation. For field/setter injection, the post-processor fires in postProcessProperties after the constructor returns, which is why @Autowired fields are null inside the constructor body. The reflection call uses Field.setAccessible(true) to bypass private modifiers — the JVM's memory model leaves reflective writes to final fields undefined, which is why field injection cannot use final.Deep understanding of the container's wiring mechanism and when injection actually occurs relative to the constructor.
Thinking @Autowired is magic syntax; not knowing it fires after object construction, making field injection unavailable in the constructor body.
Explain how @Autowired on a constructor skips the field-injection phase entirely and why that makes constructor injection more testable.
Interview-ready guide
Interview answer
When you annotate a field with @Autowired, Spring does not touch the compiler — it delegates to a BeanPostProcessor that runs after your object is constructed and uses Java reflection to write into private fields. That is the whole mechanism.
The processor
AutowiredAnnotationBeanPostProcessoris registered automatically with@SpringBootApplication; it scans every bean class for@Autowired,@Value, and@Inject- For constructor injection, this step is skipped — dependencies are resolved before the object exists and passed as arguments
- For field and setter injection, the processor fires in
postProcessProperties, after the constructor returns — which is why@Autowiredfields read inside a constructor body returnnull
Type resolution
- Spring searches for matching beans by type first; if exactly one matches, it is injected immediately
- With multiple type matches:
@Qualifiernarrows by name,@Primarypicks the default, and field name is the last resort - None of these resolve to one?
NoUniqueBeanDefinitionExceptionfails startup
The reflection call
Field.setAccessible(true)bypassesprivatemodifiers;Field.set(bean, resolvedDependency)writes the value directly- Field injection cannot use
final— the JVM's memory model leaves reflective writes tofinalfields undefined - Constructor injection avoids this entirely
- Injection metadata is cached per class so the reflection scan runs once per type, not once per prototype instance
Understanding this chain — BeanPostProcessor, type resolution, reflection — explains most of the my @Autowired is not working debugging sessions. Constructor injection sidesteps all of it.
How @Autowired Works Internally
Direct Answer
@Autowired is processed by AutowiredAnnotationBeanPostProcessor — a built-in BeanPostProcessor Spring registers automatically. It uses Java reflection to scan each bean's fields, setters, and constructors for the annotation, then queries the BeanFactory to resolve matching beans by type → @Qualifier → @Primary → field name. For constructor injection, Spring resolves dependencies before instantiation. For field/setter injection, the post-processor fires in postProcessProperties after the constructor returns, which is why @Autowired fields are null inside the constructor body. The reflection call uses Field.setAccessible(true) to bypass private modifiers — the JVM's memory model leaves reflective writes to final fields undefined, which is why field injection cannot use final.
Code Example
@Autowired is not magic — it is a BeanPostProcessor (AutowiredAnnotationBeanPostProcessor) that runs reflection against each bean after instantiation and injects collaborators before @PostConstruct. This minimal reproduction shows the exact sequence.
@Component
class OrderService {
private final UserRepository userRepo; // constructor injection (preferred)
@Autowired private PaymentClient payment; // field injection (discouraged)
private EmailService email;
public OrderService(UserRepository userRepo) { this.userRepo = userRepo; }
@Autowired
public void setEmail(EmailService email) { this.email = email; } // setter injection
}
// The processor Spring actually runs — simplified reproduction:
class MiniAutowiredProcessor implements BeanPostProcessor {
private final BeanFactory bf;
MiniAutowiredProcessor(BeanFactory bf) { this.bf = bf; }
@Override
public Object postProcessBeforeInitialization(Object bean, String name) {
for (Field f : bean.getClass().getDeclaredFields()) {
if (f.isAnnotationPresent(Autowired.class)) {
f.setAccessible(true);
try { f.set(bean, bf.getBean(f.getType())); } // inject by type
catch (IllegalAccessException e) { throw new BeanCreationException(name, e); }
}
}
return bean; // return AFTER injection, BEFORE @PostConstruct
}
}
- Spring picks the greediest satisfiable constructor for constructor injection — no annotation needed when there is only one.
- Resolution order: by type first, then by qualifier (
@Qualifier("name")), then by bean name as a fallback. - When multiple beans match, Spring throws
NoUniqueBeanDefinitionExceptionunless one is marked@Primary. - Prefer constructor injection: fields can be
final, circular dependencies fail fast at startup, and unit tests can call the constructor directly withoutReflectionTestUtils.
Common Mistakes & Pitfalls
Thinking @Autowired is magic syntax; not knowing it fires after object construction, making field injection unavailable in the constructor body.
Check your understanding
Answer these without looking back at the article.
- Why is constructor injection generally preferred over field injection for mandatory dependencies?
- What exception does Spring throw when it cannot satisfy a required @Autowired dependency?
- How does Spring handle circular dependencies with field injection but not with constructor injection?
- What is the difference between BeanPostProcessor and BeanFactoryPostProcessor?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.