2.3 Autowiring
← Back to Chapter 2: Spring Beans & Dependency Injection
Autowiring is Spring's automated dependency resolution mechanism. While Dependency Injection (DI) is the architectural pattern of providing dependencies from outside, autowiring is the runtime engine through which the Spring IoC container matches and binds collaborating beans together without manual configuration.
What is Autowiring?
Autowiring means Spring automatically finds the required dependency and provides it to your class.
In simple words:
You don't create the object using new; Spring finds the required object and injects it for you.
In traditional Java applications, you explicitly create dependencies:
// Manual instantiation (tight coupling)
OrderRepository repository = new OrderRepository();
OrderService service = new OrderService(repository);
With Autowiring, you instruct Spring to inspect the dependencies required by your component and automatically locate and inject the appropriate bean from the ApplicationContext:
@Service
public class OrderService {
private final OrderRepository orderRepository;
// Spring detects OrderRepository and automatically provides the bean
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
}
What does @Autowired do?
@Autowired tells Spring:
Find a suitable bean of this type and inject it here.
Dependency Injection vs Autowiring
| Concept | What It Is | Role |
|---|---|---|
| Dependency Injection (DI) | Architectural Design Pattern | The philosophy: "A class should receive its dependencies rather than creating them." |
| Autowiring | Spring Framework Feature | The automated implementation: Spring discovers, matches, and binds those dependencies. |
The Autowiring Process
┌─────────────────────────────────────────────────────────────┐
│ Target Bean │
│ @Service │
│ public class OrderService { │
│ OrderRepository repository; │
│ } │
└──────────────────────────────┬──────────────────────────────┘
│
│ 1. Requires bean of type: OrderRepository
▼
┌─────────────────────────────────────────────────────────────┐
│ Spring IoC Container │
│ │
│ Bean Registry: │
│ ├── [OrderRepository (Singleton Bean)] │
│ ├── [PaymentGateway (Singleton Bean)] │
│ └── [EmailService (Singleton Bean)] │
│ │
│ Match found by type: OrderRepository │
└──────────────────────────────┬──────────────────────────────┘
│
│ 2. Automatically injects bean reference
▼
┌─────────────────────────────────────────────────────────────┐
│ OrderRepository is wired into OrderService at startup │
└─────────────────────────────────────────────────────────────┘
Types of Autowiring
Spring supports @Autowired across three primary target locations:
1. Constructor Injection (Recommended)
When placed on a constructor, Spring supplies the bean arguments when instantiating the class.
@Service
public class PaymentService {
private final PaymentGateway paymentGateway;
private final NotificationService notificationService;
@Autowired // Explicit annotation (optional if single constructor)
public PaymentService(PaymentGateway paymentGateway, NotificationService notificationService) {
this.paymentGateway = paymentGateway;
this.notificationService = notificationService;
}
}
[!NOTE] Single Constructor Rule (Spring 4.3+): If a Spring bean defines only one constructor, the
@Autowiredannotation is completely optional. Spring will automatically use that constructor for dependency injection. If multiple constructors exist, you must explicitly mark one with@Autowiredto instruct Spring which one to invoke.
@Service
public class ReportService {
private final Formatter formatter;
private final EmailService emailService;
// Default constructor for testing/fallback
public ReportService(Formatter formatter) {
this(formatter, null);
}
// Explicit @Autowired required because multiple constructors exist
@Autowired
public ReportService(Formatter formatter, EmailService emailService) {
this.formatter = formatter;
this.emailService = emailService;
}
}
2. Setter / Method Injection
Useful when dependencies are optional or can be reconfigured dynamically at runtime.
@Service
public class NotificationManager {
private SMSGateway smsGateway;
@Autowired
public void setSmsGateway(SMSGateway smsGateway) {
this.smsGateway = smsGateway;
}
}
[!TIP]
@Autowiredcan be placed on any configuration method, not just traditional Java bean setters matchingsetXxx()naming:@Autowiredpublic void configureServices(SMSGateway sms, EmailGateway email) {this.smsGateway = sms;this.emailGateway = email;}
3. Field Injection (Discouraged Anti-Pattern)
Direct injection into private fields via Java reflection:
@Service
public class CustomerService {
@Autowired // Avoid in production code!
private CustomerRepository customerRepository;
}
- Bypasses constructors, preventing immutability (
finalfields cannot be used). - Difficult to unit test without firing up a Spring test context or using reflection hacks.
- Violates encapsulation and conceals dependency bloat.
How Spring Resolves Autowired Beans Under the Hood
The heavy lifting of @Autowired is handled by the AutowiredAnnotationBeanPostProcessor, a core BeanPostProcessor registered automatically by Spring Boot:
@Autowired on injection point
│
▼
┌──────────────────────────────────────────────┐
│ Search BeanFactory for beans matching type │
└──────────────────────┬───────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
0 Beans 1 Bean > 1 Beans
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐
│ Check if │ │ Exact match: │ │ Check for │
│ required=false │ │ Inject bean │ │ @Primary or │
│ Yes: inject null│ └───────────────┘ │ @Qualifier │
│ No: Throw │ │ │
│ NoSuchBean... │ │ Match: Inject │
└─────────────────┘ │ None: Throw │
│ NoUniqueBean... │
└─────────────────┘
- Type Matching (
byType): Spring scans the container for candidate beans matching the target class or interface. - Single Match: If exactly one matching bean exists, Spring injects it immediately.
- Zero Matches:
- If required (default), Spring throws
NoSuchBeanDefinitionException. - If marked optional, Spring assigns
nullor an empty container.
- If required (default), Spring throws
- Multiple Matches:
- If one bean is marked with
@Primary, it takes priority. - If an
@Qualifieris present, Spring matches the qualifier name. - Spring falls back to matching the parameter or field name against candidate bean names (
byName). - If ambiguity persists, Spring throws
NoUniqueBeanDefinitionException.
- If one bean is marked with
@Primary
We can mark one implementation as the default.
@Service
@Primary
public class CreditCardPaymentService
implements PaymentService {
}
Now when Spring sees:
PaymentService paymentService
it chooses the @Primary implementation.
PaymentService
|
------------
| |
@Primary UPI
CreditCard
↓
selected
@Qualifier
@Qualifier lets us explicitly specify which Bean we want.
@Service("upiPayment")
public class UpiPaymentService
implements PaymentService {
}
Then:
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(
@Qualifier("upiPayment")
PaymentService paymentService) {
this.paymentService = paymentService;
}
}
@Primary vs @Qualifier
| @Primary | @Qualifier |
|---|---|
| Defines default Bean | Explicitly selects Bean |
| Useful when one implementation is preferred | Useful when specific implementation is required |
| Less explicit | More explicit |
4. Handling Optional Dependencies
By default, @Autowired is mandatory: if Spring cannot find a compatible candidate bean, application startup fails. You can handle non-mandatory dependencies using three approaches:
Approach 1: @Autowired(required = false)
@Service
public class AuditService {
private AuditStorage auditStorage;
@Autowired(required = false)
public void setAuditStorage(AuditStorage auditStorage) {
this.auditStorage = auditStorage;
}
public void log(String event) {
if (auditStorage != null) {
auditStorage.save(event);
}
}
}
Approach 2: Java 8 java.util.Optional<T> (Recommended)
Constructor injection paired with Java 8's Optional clearly signals optionality without null checks:
@Service
public class PaymentProcessor {
private final Optional<FraudDetector> fraudDetector;
public PaymentProcessor(Optional<FraudDetector> fraudDetector) {
this.fraudDetector = fraudDetector;
}
public void processPayment(Payment payment) {
fraudDetector.ifPresent(detector -> detector.validate(payment));
// Proceed with transaction...
}
}
Approach 3: @Nullable
import org.springframework.lang.Nullable;
@Service
public class EmailService {
private final TemplateEngine templateEngine;
public EmailService(@Nullable TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
}
}
5. Legacy XML Autowiring Modes
In legacy XML-based Spring configurations (beans.xml), autowiring modes were configured via the autowire attribute:
<bean id="orderService" class="com.example.OrderService" autowire="byType" />
| Autowire Mode | Resolution Strategy | Description |
|---|---|---|
no (default) | None | Manual wiring only using explicit <property ref="..."> elements. |
byName | Bean Name | Inspects bean property names and matches beans having identical IDs. |
byType | Class / Interface | Inspects property types and matches beans of the same type. |
constructor | Constructor Types | Inspects constructor parameter types and matches beans of those types. |
In modern Spring Boot, annotation-based wiring (@Autowired) supersedes XML modes, defaulting to type-based inspection with name-based fallback.
6. @Autowired vs @Resource vs @Inject
Spring supports standard Java EE / Jakarta EE dependency injection annotations alongside its native @Autowired:
┌─────────────────┬────────────────────────────────────────────────────────┐
│ Annotation │ Standard & Primary Matching Strategy │
├─────────────────┼────────────────────────────────────────────────────────┤
│ @Autowired │ Spring-native. Matches byType first, then byName. │
│ @Resource │ Jakarta EE (JSR-250). Matches byName first, byType. │
│ @Inject │ Jakarta EE (JSR-330 / CDI). Matches byType first. │
└─────────────────┴────────────────────────────────────────────────────────┘
Detailed Comparison Matrix
| Feature | @Autowired | @Resource | @Inject |
|---|---|---|---|
| Origin | Spring Framework (org.springframework.beans) | Common Annotations (JSR-250 / Jakarta EE) | Dependency Injection for Java (JSR-330) |
| Default Matching | byType (with name fallback) | byName (falls back to byType) | byType (with qualifier fallback) |
| Target Locations | Constructors, Methods, Fields, Parameters | Fields, Setter methods (not constructors) | Constructors, Methods, Fields |
| Optional Support | required = false, Optional<T>, @Nullable | No built-in required attribute | Requires Provider<T> or Optional<T> |
| Disambiguation | @Qualifier("name") | Matches via name attribute: @Resource(name = "x") | @Named("name") or custom qualifiers |
| Dependency | Included in Spring (spring-beans) | jakarta.annotation:jakarta.annotation-api | jakarta.inject:jakarta.inject-api |
Example Using @Resource:
@Service
public class OrderService {
// Matches bean with id="sqlOrderRepository", not MongoOrderRepository
@Resource(name = "sqlOrderRepository")
private OrderRepository orderRepository;
}
7. Real-World Gotchas & Interview Traps
1. The new Operator Trap
If you instantiate a class with new, Spring's container has no knowledge of the object. Any @Autowired dependencies inside it will remain null:
// ❌ WRONG: NullPointerException at runtime!
UserService service = new UserService();
service.doSomething(); // orderService inside UserService is null!
// ✅ CORRECT: Let Spring inject the managed bean
@Autowired
private UserService userService;
2. Autowiring static Fields Fails
Spring cannot inject dependencies directly into static fields because static members belong to the class rather than a specific bean instance:
// ❌ WRONG: jwtTokenService remains null
@Service
public class SecurityUtils {
@Autowired
private static JwtTokenService jwtTokenService;
}
Solution: Use a non-static setter to initialize the static field:
// ✅ CORRECT: Setter-based static initialization
@Component
public class SecurityUtils {
private static JwtTokenService jwtTokenService;
@Autowired
public void setJwtTokenService(JwtTokenService service) {
SecurityUtils.jwtTokenService = service;
}
public static boolean validate(String token) {
return jwtTokenService.isValid(token);
}
}
3. Missing Default Constructor with Multiple Constructors
If you declare multiple constructors without specifying @Autowired, Spring cannot guess which constructor to call during startup and throws NoSuchMethodException. Always mark the intended injection constructor with @Autowired.
8. Summary Checklist
✔ Use constructor injection without @Autowired for single-constructor classes.
✔ Use @Autowired explicitly when multiple constructors are defined.
✔ Prefer Optional<T> or @Nullable over @Autowired(required = false) for optional dependencies.
✔ Avoid field injection in production code; it hinders unit tests and immutability.
✔ Remember that @Autowired matches byType first, then byName as a fallback.
✔ Never instantiate Spring beans manually using new if they contain autowired fields.
Next Up: Learn how to manage bean creation, memory allocation, and destroy phases in 2.4 Bean Scopes & Lifecycle.