Why @Autowired Is Discouraged and When to Prefer @Resource or Constructor Injection
This article explains why the @Autowired field injection in Spring is often discouraged, compares it with @Resource and constructor injection, shows practical code examples and error scenarios, and provides guidance on choosing the most appropriate dependency‑injection method for robust backend development.
Introduction
In Spring, dependency injection (DI) is essential for wiring beans. The @Autowired annotation is the most familiar way to inject a bean, but IntelliJ IDEA frequently warns that its usage is not recommended. This article examines the reasons behind that warning and clarifies the proper use of field injection.
Why @Autowired Is Not Recommended
Implicit Dependency
@Autowired injects dependencies by type, which can make the required bean ambiguous, especially when multiple candidates of the same type exist, leading to injection failures or wrong beans being injected.
Circular Dependency
Field injection can cause circular dependencies in singleton beans; although Spring provides mechanisms to resolve them, additional handling is still required.
Opaque Lifecycle
Beans injected via @Autowired are managed by Spring, so the class cannot explicitly see the bean’s lifecycle. Constructor injection, by contrast, makes the lifecycle explicit.
Violates Immutability
Field injection allows dependencies to change during an object’s lifetime, whereas constructor injection fixes dependencies at creation time, adhering to immutability principles.
Because of these concerns, many developers replace @Autowired with @Resource, which does not trigger the IDE warning.
@Autowired and @Resource Basic Usage
@Autowired
@Autowired is a Spring annotation that automatically injects a bean based on its type (or name when used with @Qualifier).
@Autowired
private UserService userService;In this example Spring injects the UserService bean into the userService field.
@Resource
@Resource belongs to the Java specification (javax/jakarta.annotation) and prefers name‑based injection first; if no matching name is found, it falls back to type‑based injection.
@Resource
private UserService userService;The main difference is that @Resource first looks for a bean named userService before considering the type.
Functionally the two annotations behave the same; only the matching order differs.
Practical Comparison
Consider an interface UserService with two implementations:
@Service
public interface UserService {
// user service methods
}
@Component
public class UserServiceImpl implements UserService {
// implementation
}
@Component
public class AnotherUserServiceImpl implements UserService {
// another implementation
}Testing with @Autowired
Injecting @Autowired in a test controller:
@RestController("test")
public class TestController {
@Autowired
private UserService userService;
}Running the application yields the error “required a bean but found two beans”. The issue is resolved by specifying the bean name with @Qualifier:
@RestController("test")
public class TestController {
// @Qualifier("userServiceImpl")
@Autowired
private UserService userServiceImpl;
}After the change the application starts normally and the bean is retrieved successfully.
Testing with @Resource
Injecting @Resource produces the same duplicate‑bean error:
@RestController("test")
public class TestController {
@Resource
private UserService userService;
}Fix it by naming the bean explicitly:
@RestController("test")
public class TestController {
// @Resource(name = "userServiceImpl")
@Resource
private UserService userServiceImpl;
}The project starts correctly and the bean is obtained.
Both annotations ultimately achieve the same effect; @Autowired may have a slight performance edge due to finer‑grained auto‑wiring control.
Recommended Injection Method
Constructor injection is the preferred approach, especially for complex dependency graphs, immutable objects, and unit testing.
Reasons
Explicit dependencies: Constructor parameters list all required beans, making the code clearer.
Immutability: Dependencies can be declared final, preventing later modification.
Avoids circular dependencies: Problems are detected early during bean creation.
Clear dependency graph: Improves readability and maintainability.
Example
@RestController("test")
public class TestController {
private final UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
}
// Lombok alternative
@RestController("test")
@RequiredArgsConstructor
public class TestController {
private final UserService userService;
}The example shows how constructor injection makes the dependency explicit and can be simplified with Lombok.
Is Constructor Always Better Than @Autowired?
Simplifies code: No need to write constructors or setters for each dependency.
Rapid development: Direct field injection is quick and flexible.
Automatic dependency management: Spring handles selection and injection.
Avoids long constructors: When many dependencies exist, field injection keeps the class signature short.
Conclusion
Although IDEA warns against @Autowired, it is not inherently a bad choice. Each injection style has its own advantages and drawbacks; the decision should be based on project size, complexity, and team conventions rather than blindly following IDE suggestions.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
