Stop Using @Autowired! The Better Way to Inject Dependencies in Spring Boot 3
Since Spring Boot 3 and Spring Framework 6, the use of @Autowired
for dependency injection has been discouraged. Instead, Spring recommends using constructor-based dependency injection. But why this change, and what is the best way to inject dependencies now? Let's explore it.
What is @Autowired?
Before discussing why Spring no longer recommends @Autowired
, let's quickly review its purpose. @Autowired
is used to inject dependencies into a class. It can be applied to:
- Fields
- Constructors
- Setter methods
This allows Spring to automatically resolve and assign the required beans when the application starts.
Classic example of @Autowired
in a field:
@Service
public class MyService {
@Autowired
private MyRepository myRepository;
}
In this case, Spring injects an instance of MyRepository
when creating MyService
. While this works, it has several drawbacks, especially in larger, more complex applications.
Why Doesn’t Spring Recommend @Autowired Anymore?
1. Encourages Immutable Dependencies
When we use @Autowired
on fields, dependencies can accidentally be modified within the class, which goes against the principle of immutability. In contrast, constructor injection ensures that dependencies are assigned only once during bean creation.
2. Facilitates Unit Testing
Using constructor injection makes it much easier to provide mock dependencies for unit testing, as they can be explicitly passed into the constructor without needing Spring.
Example with @Autowired
:
public class MyServiceTest {
@InjectMocks
private MyService myService;
@Mock
private MyRepository myRepository;
@BeforeEach
void setup() {
MockitoAnnotations.openMocks(this);
}
}
Example with constructor injection (no need for @InjectMocks
):
public class MyServiceTest {
private MyService myService;
@Mock
private MyRepository myRepository;
@BeforeEach
void setup() {
myService = new MyService(myRepository);
}
}
3. Avoids Reflection Issues
When @Autowired
is used on private fields, Spring uses reflection to inject dependencies, which can impact performance and code clarity. Constructor injection is a cleaner and more efficient alternative.
The New Recommended Approach: Constructor Injection
In Spring Boot 3, the best practice is to inject dependencies via constructors and omit @Autowired
, as Spring implicitly applies it in classes with a single constructor.
Example:
@Service
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
Benefits of this approach:
- Immutability:
myRepository
isfinal
, preventing accidental reassignments. - Readability: The code explicitly states its dependencies.
- Easy to test: Mocks can be passed without additional tools.
Exception: Multiple Optional Dependencies
If a dependency is optional, @Autowired
can still be used in the constructor along with @Nullable
or Optional
:
@Service
public class MyService {
private final MyRepository myRepository;
private final Optional<AuditService> auditService;
@Autowired
public MyService(MyRepository myRepository, @Nullable AuditService auditService) {
this.myRepository = myRepository;
this.auditService = Optional.ofNullable(auditService);
}
}
Conclusion
Spring Boot 3 and Spring Framework 6 have made an effort to improve security, testability, and code clarity by promoting constructor-based dependency injection instead of @Autowired
on fields. This change not only enhances immutability but also simplifies unit testing and improves application performance.
If you’re still using @Autowired
on fields, now is a great time to migrate to constructor injection and take advantage of modern Spring best practices.