What does the Autowired annotation do
By default, @Autowired treats all dependencies as mandatory (required = true). If no matching bean is found, Spring aborts startup with NoSuchBeanDefinitionException.
🟢 Junior Level
@Autowired is Spring’s primary annotation for Dependency Injection (DI). It instructs the Spring IoC container to automatically locate a matching bean in the ApplicationContext and inject it into the dependent component.
Where @Autowired Can Be Applied
- Constructors (Recommended & Best Practice):
@Service public class OrderService { private final PaymentClient paymentClient; // Since Spring 4.3 / Spring Boot 1.4+, @Autowired is OPTIONAL on single constructors! public OrderService(PaymentClient paymentClient) { this.paymentClient = paymentClient; } } - Setter Methods (Setter Injection):
@Autowired public void setPaymentClient(PaymentClient paymentClient) { this.paymentClient = paymentClient; } - Fields (Field Injection — Strongly Discouraged):
@Autowired private PaymentClient paymentClient;
Optional Dependencies (required = false)
By default, @Autowired treats all dependencies as mandatory (required = true). If no matching bean is found, Spring aborts startup with NoSuchBeanDefinitionException.
To make a dependency optional, Spring provides three approaches:
// Approach 1: required flag (field remains null if missing)
@Autowired(required = false)
private AuditService auditService;
// Approach 2: Java 8 Optional
@Autowired
private Optional<AuditService> auditService;
// Approach 3: JSR-305 / Jakarta @Nullable
@Autowired
public void setAuditService(@Nullable AuditService auditService) {
this.auditService = auditService;
}
🟡 Middle Level
The 5-Step Injection Resolution Algorithm
When Spring encounters an @Autowired injection point, AutowiredAnnotationBeanPostProcessor coordinates resolution across five deterministic steps:
[ @Autowired Injection Point ]
│
▼
Step 1: Find all candidate beans by TYPE (byType)
│
┌─────────────────┴─────────────────┐
▼ ▼
[ Exactly 1 Found ] [ Multiple Found ]
│ │
│ ▼
│ Step 2: Filter by @Qualifier
│ │
│ ┌─────────┴─────────┐
│ ▼ ▼
│ [ Exactly 1 Match ] [ Still Multiple ]
│ │ │
│ │ ▼
│ │ Step 3: Check for @Primary
│ │ │
│ │ ┌─────────┴─────────┐
│ │ ▼ ▼
│ │ [ Has @Primary ] [ No @Primary ]
│ │ │ │
│ │ │ ▼
│ │ │ Step 4: Check for @Priority
│ │ │ │
│ │ │ ┌─────────┴─────────┐
│ │ │ ▼ ▼
│ │ │ [ Has @Priority ] [ No @Priority ]
│ │ │ │ │
│ │ │ │ ▼
│ │ │ │ Step 5: Fallback by NAME (byName)
│ │ │ │ │
│ │ │ │ ┌─────────┴─────────┐
│ │ │ │ ▼ ▼
│ │ │ │ [ Name Matches ] [ No Name Match ]
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
[ INJECTION SUCCESSFUL ] [ NoUniqueBeanDefinitionException ]
- Resolution
byType: The container inspectsDefaultListableBeanFactoryfor all beans assignable to the target type. If exactly one candidate exists, it is injected immediately. - Filter via
@Qualifier: If multiple candidates exist, Spring filters candidates matching the explicit@Qualifier("beanName")identifier. - Filter via
@Primary: If multiple candidates remain, Spring checks if one candidate is annotated with@Primary. - Filter via
@Priority: If no@Primaryis present, Spring inspects the standardjakarta.annotation.Priorityannotation (the candidate with the lowest numerical integer value wins). - Fallback
byName: If ambiguity persists, Spring compares the target parameter or field variable name against the bean names registered in the context. - Failure: If none or more than one candidate remains, Spring throws
NoUniqueBeanDefinitionException.
Injecting Collections and Maps
@Autowired can inject all implementations of an interface simultaneously:
@Service
public class PaymentRouter {
// 1. Injects ALL beans implementing PaymentGateway:
// Elements are automatically sorted if annotated with @Order or implementing Ordered!
@Autowired
private List<PaymentGateway> paymentGateways;
// 2. Injects a Map where Key = Bean Name, Value = Bean Instance:
@Autowired
private Map<String, PaymentGateway> paymentGatewayMap;
}
🔴 Senior Level
Under the Hood: AutowiredAnnotationBeanPostProcessor
The resolution and injection of @Autowired is driven by AutowiredAnnotationBeanPostProcessor (AABPP):
- Metadata Collection Phase (
MergedBeanDefinitionPostProcessor):- During
postProcessMergedBeanDefinition, Spring reflects over the class hierarchy to discover fields and methods annotated with@Autowired,@Value, andjakarta.inject.Inject. - Discovered injection targets are cached inside an internal
InjectionMetadatastructure (composed ofAutowiredFieldElementandAutowiredMethodElementdescriptors). Caching eliminates redundant reflection when prototypes or multiple instances are instantiated.
- During
- Property Population Phase (
InstantiationAwareBeanPostProcessor):- During
populateBean(), Spring callspostProcessProperties(). - AABPP retrieves the cached
InjectionMetadataand callsbeanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter). - Once resolved, Spring invokes
field.setAccessible(true)andfield.set(bean, dependency)via reflection.
- During
Why @Autowired on static Fields Fails Silently
A frequent developer trap is attempting to inject dependencies into static utility fields:
@Service
public class UtilityService {
// ❌ FATAL MISTAKE: client will remain null!
@Autowired
private static DatabaseClient client;
}
Why This Fails:
AutowiredAnnotationBeanPostProcessor is a Bean Post-Processor. It executes on heap instances during the bean lifecycle’s populateBean() phase after an individual instance has been instantiated. Static fields belong to the java.lang.Class object in the ClassLoader, not to an instance in the heap. Spring explicitly ignores static fields during injection.
Workaround (If Static Access is Unavoidable):
Use a non-static setter method:
@Service
public class UtilityService {
private static DatabaseClient client;
@Autowired
public void setClient(DatabaseClient client) {
UtilityService.client = client; // Assigns instance reference to static field
}
}
Advanced Resolution via ObjectProvider<T>
ObjectProvider<T> (introduced in Spring 4.3 / 5.0) provides a modern, type-safe alternative to @Autowired(required = false):
@Service
public class MetricsReporter {
private final ObjectProvider<PushGatewayClient> clientProvider;
public MetricsReporter(ObjectProvider<PushGatewayClient> clientProvider) {
this.clientProvider = clientProvider;
}
public void report() {
// 1. Safe null-checked resolution (does not throw NoSuchBeanDefinitionException)
PushGatewayClient client = clientProvider.getIfAvailable();
if (client != null) {
client.push();
}
// 2. Safe default fallback using Supplier lambda
PushGatewayClient safeClient = clientProvider.getIfAvailable(NoOpClient::new);
// 3. Process stream of all available beans in @Order sequence:
clientProvider.orderedStream().forEach(PushGatewayClient::flush);
}
}
4 Tricky Questions
1. What is the exact sequence of steps Spring executes to resolve a dependency when encountering @Autowired?
Answer: Spring follows a strict 5-tier resolution pipeline:
byType: Queries theBeanFactoryfor all candidate beans assignable to the declared target type.@QualifierFilter: If multiple candidates are found, filters beans matching the specified@Qualifiername.@PrimaryCheck: If ambiguity remains, searches for a candidate annotated with@Primary.@PriorityCheck: If no@Primaryexists, selects the candidate with the lowest integer value onjakarta.annotation.Priority.- Fallback
byName: Matches the field or parameter variable name directly against registered bean names. If more than one candidate survives or zero candidates exist withoutrequired = false, Spring throwsNoUniqueBeanDefinitionExceptionorNoSuchBeanDefinitionException.
2. Why does placing @Autowired on a static field fail silently and leave the field as null?
Answer:
Spring manages instances of objects in the application context. Dependency injection is performed by AutowiredAnnotationBeanPostProcessor during the populateBean() phase of the bean lifecycle, which operates strictly on heap object instances.
static fields belong to the java.lang.Class object loaded in the JVM ClassLoader, not to any managed bean instance. Spring’s injection processor deliberately ignores static fields and methods to prevent race conditions and preserve container encapsulation.
3. What happens if you inject List<PaymentService> and three beans implement that interface? How is the order determined?
Answer:
Spring detects that the target is a collection type, retrieves all three beans assignable to PaymentService, and injects them as an ArrayList<PaymentService>.
The order of elements inside the list is determined by:
- The
@Order(int)annotation on the candidate classes or@Beanmethods. - The
org.springframework.core.Orderedinterface. - The standard
jakarta.annotation.Priorityannotation.
Candidates with lower numerical values (e.g. @Order(1)) are positioned first in the list. Unordered beans are placed at the end in non-deterministic order.
4. What advantages does ObjectProvider<T> provide over @Autowired(required = false)?
Answer:
- Avoids
NullPointerExceptions:@Autowired(required = false)assignsnullto the field when no bean is found.ObjectProviderprovides functional methods likegetIfAvailable(Supplier)to supply defaults safely. - Consumer Callbacks: Supports
ifAvailable(Consumer<T>)to execute logic only if the bean is present. - On-Demand Lazy Resolution: Defers bean instantiation until
getObject()orgetIfAvailable()is called. - Streaming Collections: Provides
orderedStream()to iterate over and filter matching beans in@Ordersequence.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
@Autowiredprovides automated dependency injection (DI) in Spring, processed under the hood byAutowiredAnnotationBeanPostProcessor.
- Resolution Algorithm: Resolves
byTypefirst, then narrows candidates via@Qualifier$\to$@Primary$\to$@Priority$\to$byNamefallback. ThrowsNoUniqueBeanDefinitionExceptionon unresolved ambiguity.- Constructor Injection: The recommended industry standard;
@Autowiredis optional on single-constructor classes since Spring 4.3.- Collection Injection: Injects
List<T>(sorted by@Order) orMap<String, T>(bean name $\to$ bean instance).- Modern Alternative: Use
ObjectProvider<T>instead of@Autowired(required = false)for lazy, safe, and streamable injection.- Limitation: Does not work on
staticfields.
Candidate Resolution Priority
| Step | Rule | Resolves When… |
|---|---|---|
| 1 | byType |
Exactly one bean matches target type |
| 2 | @Qualifier |
Bean matches specified qualifier name |
| 3 | @Primary |
One candidate has @Primary |
| 4 | @Priority |
Candidate has lowest numerical priority |
| 5 | byName |
Field/parameter name matches bean name |
Red Flags (DO NOT Say)
- ❌ “Spring resolves
@Autowireddependencies by bean name first.” (It always resolvesbyTypefirst;byNameis only the last fallback). - ❌ “You must place
@Autowiredon every constructor in Spring Boot 3.” (It is optional on single-constructor classes). - ❌ “You can annotate
staticfields with@Autowiredto initialize utility classes.” (Static fields are ignored by Spring’s bean post-processors and remainnull). - ❌ “Field injection is faster than constructor injection.” (Field injection incurs heavy runtime reflection costs via
setAccessible(true)).