Field Injection and Why to Avoid It in Spring Boot
📖 Scenario: You are building a simple Spring Boot service that sends notifications. You want to learn how to inject dependencies properly and understand why field injection is discouraged.
🎯 Goal: Create a Spring Boot service class that uses constructor injection instead of field injection to get a NotificationSender bean.
📋 What You'll Learn
Create a NotificationSender interface with a method send(String message).
Create a class EmailNotificationSender that implements NotificationSender.
Create a service class NotificationService that uses constructor injection to get NotificationSender.
Avoid using field injection (@Autowired on fields).
💡 Why This Matters
🌍 Real World
In real Spring Boot applications, managing dependencies clearly helps maintain and test code effectively.
💼 Career
Understanding dependency injection patterns is essential for writing clean, testable, and maintainable Spring Boot applications.
Progress0 / 4 steps
1
Create NotificationSender interface and EmailNotificationSender class
Create an interface called NotificationSender with a method void send(String message). Then create a class called EmailNotificationSender that implements NotificationSender and overrides the send method with an empty body.
Spring Boot
Hint
Use public interface NotificationSender and public class EmailNotificationSender implements NotificationSender. Annotate EmailNotificationSender with @Component so Spring can find it.
2
Add NotificationService class with constructor injection
Create a class called NotificationService annotated with @Service. Add a private final field called notificationSender of type NotificationSender. Create a constructor that takes a NotificationSender parameter and assigns it to the field.
Spring Boot
Hint
Use @Service annotation and create a constructor that takes NotificationSender and assigns it to the private final field.
3
Explain why to avoid field injection
Add a comment inside NotificationService above the field notificationSender explaining why field injection (using @Autowired on fields) is discouraged. Mention issues like difficulty in testing and hidden dependencies.
Spring Boot
Hint
Write a comment above the field explaining that field injection hides dependencies and makes testing harder, while constructor injection is better.
4
Use NotificationSender in NotificationService method
Add a public method notifyUser(String message) in NotificationService that calls send(message) on the notificationSender field.
Spring Boot
Hint
Create a method notifyUser that calls send on the injected notificationSender.
Practice
(1/5)
1. What is a main reason to avoid using @Autowired directly on fields in Spring Boot?
easy
A. It disables Spring Boot auto-configuration
B. It causes syntax errors in the code
C. It requires manual object creation
D. It hides dependencies and makes testing harder
Solution
Step 1: Understand field injection behavior
Field injection uses @Autowired on fields, which hides the dependencies inside the class without showing them in the constructor.
Step 2: Analyze impact on testing
Because dependencies are hidden, it becomes harder to create test instances with mocks or stubs, reducing testability.
Final Answer:
It hides dependencies and makes testing harder -> Option D
Quick Check:
Hidden dependencies = avoid field injection [OK]
Hint: Hidden dependencies make testing tough, avoid field injection [OK]
Common Mistakes:
Thinking field injection causes syntax errors
Believing field injection disables auto-configuration
Assuming field injection requires manual object creation
2. Which of the following is the correct way to inject a dependency using constructor injection in Spring Boot?
easy
A. @Autowired private Service service;
B. Service service = new Service();
C. public MyClass(Service service) { this.service = service; }
D. @Autowired public void setService(Service service) { this.service = service; }
Solution
Step 1: Identify constructor injection syntax
Constructor injection requires a constructor that accepts the dependency as a parameter and assigns it to a field.
Step 2: Match the correct code
public MyClass(Service service) { this.service = service; } shows a constructor receiving Service and assigning it, which is the correct pattern.
Final Answer:
public MyClass(Service service) { this.service = service; } -> Option C
Quick Check:
Constructor injection = constructor with parameters [OK]
Hint: Constructor injection uses constructor parameters, not field annotations [OK]
Common Mistakes:
Confusing field injection with constructor injection
Using manual object creation instead of injection
Using setter injection when constructor injection is asked
3. Given this Spring Boot class using field injection:
public class MyService {
@Autowired
private Repository repo;
public String getData() {
return repo.fetch();
}
}
What is a likely problem when writing unit tests for MyService?
medium
A. The getData() method will not compile
B. You cannot replace repo with a mock easily
C. The repo field will always be null at runtime
D. Spring Boot will throw a runtime exception on startup
Solution
Step 1: Understand field injection impact on testing
Field injection hides the dependency inside the private field, making it hard to inject a mock instance during unit tests.
Step 2: Analyze testability of MyService
Since repo is private and injected by Spring, test code cannot easily replace it with a mock, limiting test control.
Final Answer:
You cannot replace repo with a mock easily -> Option B
Quick Check:
Field injection blocks mock replacement [OK]
Hint: Field injection hides fields, blocking mock injection in tests [OK]
Common Mistakes:
Assuming compilation errors occur
Thinking Spring throws exceptions on field injection
Believing the field is always null at runtime
4. Identify the problem in this Spring Boot class using field injection:
public class UserService {
@Autowired
private UserRepository userRepository;
public UserService() {}
public User findUser(Long id) {
return userRepository.findById(id).orElse(null);
}
}
medium
A. Field injection hides dependencies making testing harder
B. Constructor is missing the @Autowired annotation
C. The userRepository field should be static
D. The findUser method should be static
Solution
Step 1: Review injection style
The class uses field injection with @Autowired on a private field, which hides dependencies.
Step 2: Understand testing impact
Because dependencies are hidden, it is harder to inject mocks or stubs for unit testing, reducing testability.
Final Answer:
Field injection hides dependencies making testing harder -> Option A
Quick Check:
Field injection = hidden dependencies = testing difficulty [OK]
Hint: Field injection hides dependencies, causing test problems [OK]
Common Mistakes:
Thinking constructor needs @Autowired always
Believing fields must be static for injection
Assuming methods must be static for injection
5. You have a Spring Boot service class that uses field injection:
public class OrderService {
@Autowired
private PaymentService paymentService;
public boolean processOrder(Order order) {
return paymentService.charge(order.getAmount());
}
}
How would switching to constructor injection improve this class?
hard
A. It makes dependencies explicit and improves testability
B. It removes the need to annotate fields with @Autowired
C. It automatically creates new instances of dependencies
D. It allows Spring to inject dependencies without a constructor
Solution
Step 1: Understand constructor injection benefits
Constructor injection requires dependencies to be passed in explicitly, making them visible and clear in the class design.
Step 2: Analyze testability improvements
Explicit dependencies allow easy replacement with mocks or stubs during testing, improving testability and code clarity.
Final Answer:
It makes dependencies explicit and improves testability -> Option A