@Autowired is the annotation most beginners meet first and understand last. It looks like magic:
you declare a field, add one line above it, and suddenly a fully built object appears. There is no
magic — just a container matching your request against the beans it manages. This tutorial shows
exactly what that matching does, the three places you can apply it, and how to fix the errors it
produces.
What @Autowired asks Spring to do
At its core, @Autowired is a request: "find a bean in the container whose type is assignable to
this target, and put it here." It is the visible face of
dependency injection, which is itself the
concrete mechanism behind inversion of control.
You never call new on the dependency; Spring supplies an instance it already created and manages.
Because resolution is driven by type, the target's declared type is what matters. If you autowire
a NotificationService and exactly one bean of that type exists, Spring injects it. The
annotation says nothing about which concrete class — that is decided by what is registered in
the container.
The three injection styles
You can apply @Autowired in three positions, and the choice has real consequences.
Field injection
@Service
public class OrderService {
@Autowired
private InventoryClient inventory; // concise, but hard to test
}
This is the shortest form and the one tutorials overuse. Its problem is that the field is private
and non-final, so a unit test cannot supply a fake InventoryClient without reflection or a full
Spring context.
Setter injection
@Service
public class OrderService {
private InventoryClient inventory;
@Autowired
public void setInventory(InventoryClient inventory) {
this.inventory = inventory;
}
}
Setter injection suits genuinely optional dependencies and allows reconfiguration after construction, but it leaves the object in a half-built state between construction and the setter call.
Constructor injection
@Service
public class OrderService {
private final InventoryClient inventory;
// @Autowired is optional here — single constructor
public OrderService(InventoryClient inventory) {
this.inventory = inventory;
}
}
This is the style to standardise on. The dependency is final, guaranteed to be present, visible
in the signature, and trivially supplied in a test: new OrderService(fakeClient). Since Spring
4.3, a class with a single constructor does not even need the annotation.
Pro tip: If your constructor is growing to five or six parameters, that is not a reason to switch back to field injection — it is a design smell telling you the class has too many responsibilities. Split it before you hide the problem.
How Spring picks a bean when types collide
The interesting behaviour appears when more than one bean matches the requested type. Suppose you have two payment implementations:
public interface PaymentProcessor { void pay(double amount); }
@Component
public class UpiProcessor implements PaymentProcessor {
public void pay(double amount) { System.out.println("UPI: " + amount); }
}
@Component
public class CardProcessor implements PaymentProcessor {
public void pay(double amount) { System.out.println("Card: " + amount); }
}
Now this fails at startup:
@Service
public class Checkout {
@Autowired
private PaymentProcessor processor; // NoUniqueBeanDefinitionException
}
Spring finds two candidates for PaymentProcessor and refuses to guess. You resolve it in one of
three ways: mark one bean @Primary, match by name, or state the exact bean with @Qualifier.
@Service
public class Checkout {
private final PaymentProcessor processor;
public Checkout(@Qualifier("upiProcessor") PaymentProcessor processor) {
this.processor = processor;
}
}
The qualifier value is the bean name, which defaults to the class name with a lowercase first
letter — UpiProcessor becomes upiProcessor. This resolution flow, matching by type then
narrowing by qualifier, is the single most common Spring interview follow-up on this topic.
Common mistake: Beginners assume
@Qualifiermatches by the interface or by a label they invented. It matches the bean name. If you rename the component or register it with a custom name, the qualifier string must change with it, or injection breaks.
Optional dependencies and collections
Sometimes a dependency genuinely may not exist. required = false leaves the field null when no
bean is found, though modern code prefers Optional:
@Service
public class AuditService {
private final Optional<MetricsPublisher> metrics;
public AuditService(Optional<MetricsPublisher> metrics) {
this.metrics = metrics; // empty if no MetricsPublisher bean exists
}
}
You can also autowire all beans of a type into a List or Map. Injecting
List<PaymentProcessor> gives you every implementation, which is a clean way to build plugin-style
designs where each processor registers itself simply by being a component.
Circular dependencies and how they surface
A trap worth knowing before you hit it in production: if bean A needs bean B and bean B needs bean A, you have a circular dependency. With constructor injection this fails hard at startup, because neither object can be constructed before the other exists:
@Service
public class A {
public A(B b) { } // needs B first
}
@Service
public class B {
public B(A a) { } // needs A first — deadlock at startup
}
Spring throws a BeanCurrentlyInCreationException and refuses to start. That failure is a gift —
it forces you to fix a genuine design problem. Field or setter injection can sometimes hide the
cycle by injecting a half-built bean later, which lets the app start but leaves a fragile
initialisation order.
The right fix is almost never a technical workaround like @Lazy; it is to break the cycle. Extract
the shared logic into a third bean that both A and B depend on, so the dependency graph becomes a
tree again. That constructor injection exposes the cycle at startup, while field injection can mask
it, is one more argument for preferring constructor injection.
Interview note: If asked "how does constructor injection help with circular dependencies", the precise answer is that it does not resolve them — it detects them at startup instead of letting a broken object graph run silently. Framing it as detection rather than resolution shows real understanding.
Testing benefits you feel immediately
The strongest everyday argument for constructor injection is testing. Because dependencies arrive as parameters, you can build the object in a plain unit test with no Spring context at all:
class OrderServiceTest {
@Test
void appliesDiscount() {
InventoryClient fake = new FakeInventoryClient();
OrderService service = new OrderService(fake); // no Spring needed
// assert on service behaviour with the fake
}
}
Try that with field injection and you cannot set the private field without reflection or a running container, which makes fast unit tests painful. This single practical difference is why code reviewers flag field injection: it quietly makes the class harder to test, and untested services are where bugs hide.
Where @Autowired sits in the bean's life
Injection is not the first thing that happens to a bean. Spring calls the constructor, then
performs autowiring, and only then runs initialisation callbacks — the full order is covered in
the bean lifecycle tutorial. This is why an
@Autowired field is still null inside the constructor with field injection, and why constructor
injection sidesteps the problem entirely: the value arrives as a parameter, before the object even
exists.
Every autowired object is a managed Spring bean pulled from the same container, so the same resolution rules apply whether you are wiring a repository into a service or a service into a controller across a whole Spring Boot application.
Interview relevance
Expect to be asked to compare the three injection styles and justify a preference — the strong
answer is constructor injection for immutability and testability. Be ready to explain what happens
when two beans match the same type, and to name the three resolution tools (@Primary, bean-name
matching, @Qualifier). A common trap is asking whether @Autowired is mandatory on a
single-argument constructor; the correct answer is no, not since Spring 4.3. Practise these
alongside the broader Spring Boot annotations interview questions
so you can move smoothly from @Autowired to @Component, @Service and @Configuration.
Frequently Asked Questions
Is @Autowired required on constructors in modern Spring?
What is the difference between field and constructor injection?
How does Spring resolve @Autowired when two beans match?
What does @Autowired(required = false) do?
Why is constructor injection preferred over field injection?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

